选择排序 O(n^2)
void Csort(int * data,int length)
{
int index;//当前最小值的索引
int item;//当前最小值
for (int i = 0; i < length; i++)
{
item = data[i];
index = i;
for (int w = i + 1; w < length; w++)
{
if (item > data[w])//如果找到更小的值,更新最小值
{
item = data[w];
index = w;
}
}
data[index] = data[i];//将最小值放在相应位置
data[i] = item;//
}
}
插入排序 O(n^2)
template<typename T>
void insertion_sort(T* A, int len) {
T key;
int i, j;
for ( i = 1; i < len; i++) {
key = A[i];
j = i - 1;
while ((j >= 0) && (key < A[j])) {
A[j + 1] = A[j];
A[j] = key;
j--;
}
}
}
归并排序 O(nlogn)
伪代码:
void merge_sort(int q[],int l,int r)
{
if(l >= r)return ; //递归结束条件
int mid=l+r>>1; //折半递归
merge_sort(q,l,mid); //左边递归
merge_sort(q,mid+1,r); //右边递归
int k=0,i=l,j=mid+1; //tmp是一个暂时存储元素的数组
while(i<=mid&&j<=r)
if(q[i] <= q[j]) tmp[k++]=q[i++];
else tmp[k++]=q[j++];
while(i<=mid) //右半部分剩余没有合并的元素
tmp[k++] = q[i++];
while(j<=r) //左半部分剩余没有合并的元素
tmp[k++] = q[j++];
for(i=l,j=0;i<=r;i++,j++)
q[i] = tmp[j]; //将存在tmp数组的元素重新放回原数组中
}
快速排序
void quick_sort(int* a,int l, int r)//a为待细分组,l和r分别为左右索引
{
if (l >= r) return;
int mid_num = a[l]; //获取中介元素
int i = l, j = r;
while (i < j)
{
while (a[j] >= mid_num && i < j) j--; //从右向左判断,找到小于中介的值
if (i < j) a[i] = a[j];
while (a[i] <= mid_num && i < j) i++;//从左向右判断,找到大于中介的值
if (i < j) a[j] = a[i];
}
a[i] = mid_num; //循环结束,将中间值放入数组
quick_sort(a, l, i - 1);
quick_sort(a, i + 1, r);
}
算法最好最坏情况总结
算法 | 最好情况 | 最差情况 |
冒泡排序 | 已排好,不交换 | 每次比较都要交换 |
插入排序 | 排序前已排好,比较次数n-1,元素移动次数为0 | 每一次比较都要做数据移动 |
快速排序 | 每次划分对一个元素定位后,左右两侧序列长度相同,则下一步就是对两个长度减半的子序列进行排序 | 待排序元素已经按照其排序码从小到大排好序,其递归树成为单支树 |
计算时间复杂度 (递归树法、代入法、主定理法)
有关排序算法的相关问题分析
次序选择问题
#include<iostream>
#include<time.h>
using namespace std;
// 随机选取主元并且把数组按照主元分解,并返回主元最后的位置(与快排完全一致)
int Randomized_Partition(int A[], int l, int r)
{
int p = 0; // 交换媒介
int P1 = l;
int P2 = l; // 两个遍历下标
srand((time(NULL)));
int s = rand() % (r - l) + 1 + l; // 随机选取主元
// 交换主元到末尾
p = A[s];
A[s] = A[r];
A[r] = p;
// 遍历分解原数组
while (P2 < r)
{
// P2遍历到比主元小的,就把这个元素放到左边
if (A[P2] <= A[r])
{
p = A[P1];
A[P1] = A[P2];
A[P2++] = p;
++P1;
}
// 如果没有,就直接把P2往下;
else ++P2;
}
// 把主元放中间
p = A[r];
A[r] = A[P1];
A[P1] = p;
// 返回主元现在的位置
return P1;
}
// 进行次序选择
int Randomized_Selection(int A[], int k,int l, int r)
{
// 递归出口
if (l >= r)
return 0;
// 分解原数组,并返回主元位置
int p = Randomized_Partition(A, l, r);
int x = 0; // 保存结果
// 判断是否找到的主元是我们要找的第k小的位置,注意这里的比较的表达式
if (p-l+1 == k)
return A[p];
// 如果结果比k小,就找右边,注意这时候的k要减掉左边的元素
if (p - l + 1 < k)
x=Randomized_Selection(A, k - (p - l + 1), p +1, r);
else
x=Randomized_Selection(A, k, l, p-1);
return x;
}
int main()
{
int A[6] = { 3,2,5,6,0,4 };
cout << "原数组为:";
for (int i = 0; i < 6; ++i)
cout << A[i] << " ";
// 找第二小的元素
int ans=Randomized_Selection(A,2, 0, 5);
cout << "第二小的元素为:"<<ans<<endl;
return 0;
}
最大子数组 O(nlogn)
s3的求法
最大子数组各种算法比较:
//分治法
#include <iostream>
#include <climits>
using namespace std;
int a[16] = {13,-3,-25,20,-3,-16,-23, 18,20,-7,12 ,-5,-22,15,-4,7};
int findCrossbigSubarray(int low,int mid,int high) //和一定要包含a[mid]和a[mid+1]
{
int sum = 0;
int left_big = -INT_big;
for(int i=mid; i>=low; --i) //mid属于左数组
{
sum += a[i];
if(sum > left_big) left_big = sum;
}
sum = 0;
int right_big = -INT_big;
for(int i=mid+1; i<=high; ++i) //mid+1属于右数组
{
sum += a[i];
if(sum > right_big) right_big = sum;
}
return right_big + left_big;
}
int findbigSubarray(int low, int high)
{
if(low == high) return a[low];
int mid = (low + high) / 2;
int left_big = findbigSubarray(low,mid); //左数组的最大值
int right_big = findbigSubarray(mid+1,high); //右数组的最大值
int cross_big = findCrossbigSubarray(low,mid,high); //穿越数组的最大值
int big = big(left_big,right_big);
if(cross_big > big) big = cross_big;
return big; //返回三者的最大值
}
int main()
{
cout<<findbigSubarray(0,15);
return 0;
}
//动态规划算法
//动态转移方程:
//sum[i+1] = max(sum[i]+a[i+1], a[i+1])
#include <iostream>
using namespace std;
int a[16] = {13,-3,-25,20,-3,-16,-23, 18,20,-7,12 ,-5,-22,15,-4,7};
//sum = {13,10,-15,20,17, 1, -22, 18,38,31,43 ,38, 16,31,27,34};
int sum[16] = {a[0]};
int main()
{
for(int i=0; i<16; ++i)
sum[i+1] = max(sum[i]+a[i+1], a[i+1]);
//取sum中的最大值
int Max = sum[0];
for(int i=1; i<16; ++i)
if(sum[i] > Max)
Max = sum[i];
cout<<Max;
return 0;
}
逆序对计数问题
#include <bits/stdc++.h>
using namespace std;
int n, a[5000001], b[5000001];
long long ans;
inline void msort(int l, int r)//归并排序
{
int mid = (l + r) / 2;//取中间
if(l == r)//若l == r了,就代表这个子序列就只剩1个元素了,需要返回
{
return;
}
else
{
msort(l, mid);//分成l和中间一段,中间 + 1和r一段(二分)
msort(mid + 1, r);
}
int i = l;//i从l开始,到mid,因为现在排序的是l ~ r的区间且要二分合并
int j = mid + 1;//j从mid + 1开始,到r原因同上
int t = l;//数组b的下标,数组b存的是l ~ r区间排完序的值
while(i <= mid && j <= r)//同上i,j的解释
{
if(a[i] > a[j])//如果前面的元素比后面大(l ~ mid中的元素 > mid + 1 ~ r中的元素)(逆序对出现!!!)
{
ans += mid - i + 1;//由于l ~ mid和mid + 1 ~ r都是有序序列所以一旦l ~ mid中的元素 > mid + 1 ~ r中的元素而又因为第i个元素 < i + 1 ~ mid那么i + 1 ~ mid的元素都 > 第j个元素。所以+的元素个数就是i ~ mid的元素个数,及mid - i + 1(归并排序里没有这句话,求逆序对里有)
b[t++] = a[j];//第j个元素比i ~ mid的元素都小,那么第j个元素是目前最小的了,就放进b数组里
++j;//下一个元素(mid + 1 ~ r的元素小,所以加第j个元素)
}
else
{
b[t++] = a[i];//i小,存a[i]
++i;//同理
}
}
while(i <= mid)//把剩的元素(因为较大所以在上面没选)
{
b[t++] = a[i];//存进去
++i;
}
while(j <= r)//同理
{
b[t++] = a[j];
++j;
}
for(register int i = l; i <= r; ++i)//把有序序列b赋值到a里
{
a[i] = b[i];
}
return;
}
int main()
{
scanf("%d", &n);
for(register int i = 1; i <= n; ++i)
{
scanf("%d", &a[i]);
}
msort(1, n);//一开始序列是1 ~ n
printf("%lld", ans);
return 0;
}
分治法的其他应用
快速幂
double exp2(double a, int n)
{
if(a==0) return 0;
if(n<=0) return 1;
else{
int x = exp2(a,n/2);
if(n%2==1) return a*x*x;
else return x*x;
}
}
二分查找算法
int BiSearch(int a[],const int& x,int l,int r)
{
if(r>=l){
int m = (l+r)/2;
if(x==a[m]) return m;
else if(x<a[m]) return BiSearch(a,x,l,m-1);
else return BiSearch(a,x,m+1,r);
}
else return -1;
}
棋盘覆盖问题
#include<iostream>
#define S 8
using namespace std;
int number = 1;//给每个L型骨牌一个编号
int board[S][S];//储存棋盘
//t1、t2分别是棋盘左上角的横坐标和纵坐标
//d1、d2分别是棋盘内特殊方块的横坐标和纵坐标
//size表示棋盘的大小
void chessboard(int t1 ,int t2,int d1, int d2, int size) {
if (size == 1)return; //递归终止条件
int s = size / 2;//分隔棋盘
int n = number++;
//左上角小棋盘:
if ((d1 < t1 + s) && (d2 < t2 + s)) //判断棋盘内是否有特殊方块
{
chessboard(t1, t2, d1, d2, s);//如果有,就继续递归
}
else {
board[t1 + s - 1][t2 + s - 1] = n;//在棋盘右下角放一个L型骨牌,并把它当做特殊方块
chessboard(t1, t2, t1 + s - 1, t2 + s - 1, s);
}
//右上角小棋盘
if ((d1 < t1 + s) && (d2 >= t2 + s))
{
chessboard(t1, t2 + s, d1, d2, s);
}
else {
board[t1 + s - 1][t2 + s] = n;//在棋盘左下角放一个L型骨牌,并把它当做特殊方块
chessboard(t1, t2 + s, t1 + s - 1, t2 + s, s);
}
//左下角小棋盘
if (d1 >= t1 + s && d2 < t2 + s) {
chessboard(t1 + s, t2, d1, d2, s);
}
else {
board[t1 + s][t2 + s - 1] = n;//在棋盘右上角放一个L型骨牌,并把它当做特殊方块
chessboard(t1 + s, t2, t1 + s, t2 + s - 1, s);
}
//右下角小棋盘
if (d1 >= t1 + s && d2 >= t2 + s) {
chessboard(t1 + s, t2 + s, d1, d2, s);
}
else {
board[t1 + s][t2 + s] = n;//在棋盘左上角放一个L型骨牌,并把它当做特殊方块
chessboard(t1 + s, t2 + s, t1 + s, t2 + s, s);
}
}
//测试代码
int main()
{
// 将棋盘内每个方块初始化为0
for(int i = 0; i < S; i++) {
for(int j = 0; j < S; j++) {
board[i][j] = 0;
}
}
chessboard(0,0,1,2,S);
//输出测试结果
for(int i=0;i<S;i++)
{
for (int j = 0; j < S; j++) {
cout << board[i][j] << "\t";
}
cout << endl;
}
}