选择排序
在一组数据中每次选最小的放在前面。第1趟找最小的,每找到一个数据比当前数据小,则两两交换,找到最小的后放在第1个位置,第2趟在剩下的数据中找最小的放在第2个位置,第i趟,在剩下的数据中找最小的放在第i个位置,直到找完整个数组。
由于我在每一次找最小时都是从前往后遍历,前一个循环找到的最小元素一定在后一次循环找到的最小元素前面,所以这是一个稳定的排序(当然每次从后往前找最小元素就可以改成不稳定的排序)
void selectSort(int *arr, int len)
{
int tmp;
for(int i = 0; i<len; i++)
{
for(int j = i+1; j<len; j++)
{
if(arr[j]<arr[i])
{
tmp = arr[j];
arr[j] = arr[i];
arr[i] = tmp;
}
}
}
}
插入排序
如23 45 54 53 24
以23为第一个有序数组,往后遍历。到45时,比较45比前面有序数组的最后一个哪个大,由于45大于23,所以不用交换,继续向后遍历,同样方法继续向下遍历。遍历到53时,发现53比前面有序数组最后一个小,所以与54交换交换后顺序变为
23 45 53 54 24
然后比较53交换后与前面有序数组最后一个的值大小,若小于前面数组最后一个,则继续交换,然后继续与前面有序数组最后一个数值比较,知道遍历完
继续向后遍历,到下一个24,发现24比前一个有序数组的最后一个大,24与54交换。同样的方法一直与前面有序数组最后一个比较,直到值大于前面最后一个或者遍历完整个数组为止。
由于从后往前找当前比较数字与前面有序数组的最后一个值的关系,所以整个数组越有序,速度越快。时间复杂度最差O(n^2),最好O(n)。对于数值相同时,不交换顺序,所以是一个稳定的排序
void insertSort(int *arr,int len)
{
int tmp;
for(int i = 0; i<len-1; i++)
{
for(int j = i+1; j > 0; j--)
{
if(arr[j]>arr[j-1])
{
tmp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = tmp;
}
else
{
break;
}
}
}
}
希尔排序
希尔排序是基于插入排序的一种优化。首先将数据分组,如将20 21 0 7 23五个数据按2个数据分一组,在排序时两个数选一个进行插入排序,比如选第0个数“20”,第2个数“0”,第4个数“23”,进行插入排序。先将0与20比较,0大于20,两两对换,此时变成0 21 20 7 23,再将23与20比较,若后者小于前者,两两对换,保证前面小于后面,然后就做到了每组中选出的一个数有序。同样的找“1”“3”号下标的数,做比较就得到了 0 7 21 20 23。然后再将数据按4个数一组,同样的办法保证各个组选出的数有序。让后再加每组的数,直到所有数一组,这个时候就变成了完全的插入排序。
希尔排序由于不同分组可能会导致相同值的顺序改变,所以属于不稳定的排序。时间复杂度O(n^1.3)到O(n^1.5)之间。
其中,每次到底分2个,4个,8个一组或者其他的分法有待数学验证,这里只是写代码方便就写成了这个值
void shellSort(int *arr,int len)
{
int tmp;
for(int gap = len/2; gap>0; gap/=2)
{
for(int i = gap; i<len; i++)
{
for(int j = i; j-gap >= 0&&arr[j] < arr[j-gap]; j-=gap)
{
tmp = arr[j];
arr[j] = arr[j-gap];
arr[j-gap] = tmp;
}
}
}
}
快速排序
顾名思义,这个排序的方法是快,并且数据越乱越快(相比较其他排序),算法时间复杂度nlogn。
基本排序规则是每一趟从数列中选取一个数拿出来,定一个首标记和一个尾巴标记。尾标记往前走,找到比拿出数据大的数放在后面,首标记往后走,比拿出数小的数放在前面,当首尾标记重合时返回这个位置,一趟排序结束。然后再将分开的两段分别用这种方法排序。所排的区间会来越小,直到所有要排的区间数为1时排序结束。
int sortOnly(int *arr,int low,int high)
{
int tmp = arr[low];//拿出low下标数据作为本次排序的参照
while(low < high)
{
while(low < high&&arr[high] >= tmp)//每次从右向左找到比tmp小的数的下标high
{
high--;
}
if(low < high)//对于不满足arr[high] >= tmp的,用找到的比tmp小的数将low位置数覆盖。然后low往后走一步,保证low前面的数比tmp小
{
arr[low++] = arr[high];
}
else//不满足low < high的条件退出循环,本次排序结束
{
break;
}
while(low < high&&arr[low] <= tmp)//从左向右找比tmp大的数的下标low
{
low++;
}
if(low<high)//对于不满足arr[low] <= tmp的,用找到的比tmp大的数将high位置数覆盖。然后high往前走一部,保证high后面的数比tmp大
{
arr[high--] = arr[low];
}
else
{
break;
}
}
arr[low] = tmp;
return low;
}
void quick(int *arr,int low,int high)
{
int sit = sortOnly(arr,low,high);//局部排序
if(sit-1 > low)//若sit与low相隔至少两个数据,再次排序
{
quick(arr,low,sit-1);
}
if(sit+1 < high)//若sit与high相隔至少两个数据,再次排序
{
quick(arr,sit+1,high);
}
}
void quickSort(int *arr,int len)
{
assert(NULL != arr);//断言传入数组有效
quick(arr,0,len-1);//将传入数组首尾下标作为参数传入
}
上面的方式虽然完成了快排的思路,但是由于quick函数每次递归调用,空间复杂度太大,我们利用栈将递归改为非递归。
quickStack(int *arr,int low,int high)
{
std::stack<int> sta;
sta.push(low);
sta.push(high);
while(!sta.empty())
{
high = sta.top();
sta.pop();
low = sta.top();
sta.pop();
sit = sortOnly(arr,low,high);
if(sit-1>low)
{
sta.push(low);
sta.push(sit-1);
}
if(sit+1<high)
{
sta.push(sit+1);
sta.push(high);
}
}
}
堆排序
堆排序的原理是将数据看做堆。如整形数组arr[0] = {23,53,55,65,27}。我们将数组看作下图的堆
每个数据满足下列性质。
若根的下标为i,则他的左孩子下标为i*2+1,右孩子下标为i*2+2。如53下标为1,左孩子65下标为3,右孩子27下标为4;
若叶的下标为i,则他的直接双亲为(i-1)/2。如53下标为1,双亲23下标为0
堆排序完成的任务是将这个堆调整为小根堆或大根堆
小根堆(根数值最小,兄弟节点左比右小);
大根堆(根数值最大,兄弟节点左比右大);
在这里演示简历大根堆。基本思路是先局部调整。将一个局部的堆调整成根最大,比如将上图的53,65,27。如下图所示
用同样的方法,将数组最后到起始,从后向前一直调整,就能将整个数组堆的最顶端数调整为最大值。
然后再将堆顶的数与最后一个数交换。再次进行调整,这次调整的数组不包括最后一个数。反复调整,再将调整后堆顶数与当前调整的堆最后一个数交换。最终就能调整为从后到前数据依次减小了。
void heapOnly(int *arr,int parent,int len)
{
if(len < 2)
{
return ;
}
int tmp = arr[parent];
for(int i = parent*2+1; i<len; i = i*2+1)
{
if(i+1 < len&&arr[i] < arr[i+1])
{
i++;
}
if(arr[i] > tmp)
{
arr[parent] = arr[i];
parent = i;
}
else
{
break;
}
}
arr[parent] = tmp;
}
void heapSort(int *arr,int len)
{
assert(NULL != arr);
for(int i = (len-1-1)/2; i>=0; len--)
{
heapOnly(arr, i, len);
}
int tmp;
for(int i = 0; i<len; i++)
{
tmp = arr[0];
arr[0] = arr[len-1-i];
arr[len-1-i] = tmp;
heapOnly(arr, i, len-1-i);
}
}
冒泡排序
从前到后遍历,每连续两个数进行比较,大的放在后面,遍历完一次整个数组,最小的数就在最后面。下一次将不包括最后一个数的数组进行同样的遍历,找出第二大的放在倒数第二,以此循环完成从小到大排序
void bubbleSort(int *arr,int len)
{
int tmp;
for(int i = 0; i<len; i++)
{
for(int j = 0; j<len-i-1; j++)
{
if(arr[j]>arr[j+1])
{
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}