一、冒泡排序
时间复杂度:o(n^2) 空间复杂度:o(1) 稳定排序
算法:(从前往后)
1. 比较相邻的两个元素,若第一个比第二个大就交换他们
2. 从第一对比较到最后一对,最后会产生一个最大值
3. 针对除了最后一个元素在进行相同的操作
4. 持续上面的步骤,比较次数会越来越少,直到没有数据需要比较
从后往前道理是一样的,每次产生一个最小值
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 //打印函数
4 void print(int array[],int64_t size)
5 {
6 printf("this is:\n");
7 int i;
8 for(i=0;i<size;i++){
9 printf("%d ",array[i]);
10 }
11 printf("\n");
12 }
13 //交换函数
14 void swap(int *a,int *b){
15 int tmp=*a;
16 *a=*b;
17 *b=tmp;
18 }
19 //冒泡函数
20 //void bubble_sort(int array[],int64_t size) {
21 // if(size<=1){//若只有一个则直接返回
22 // return;
23 // }
24 // int64_t bound=0; //[0,bound):有序序列 [bound,size):无序序列
25 // for(bound=0;bound<size;bound++){
26 // int64_t cur=size-1; //从后往前进行比较,每次找到一个最小值
27 // for(;cur>bound;cur--){
28 // if(array[cur]<array[cur-1]){ //每次和前一个数据进行比较,若前边大于后边
29 // swap(&array[cur],&array[cur-1]);//则进行交换
30 // }//end if
31 // }//end for
32 // }//end for return;
33 //}
34 void bubble_sort(int array[],int64_t size)
35 {
36 if(size<=1){//若只有一个则直接返回
37 return;
38 }
39 int64_t bound=size-1; //从前往后,每次找到一个最大值 (bound,size]是有序序列
40 for(;bound>=0;bound--){
41 int64_t cur=1;
42 for(;cur<=bound;cur++){
43 if(array[cur]<array[cur-1])
44 swap(&array[cur],&array[cur-1]);
45
46 }
47 }
48 }
49 int main()
50 {
51 int array[]={9,5,2,7};
52 bubble_sort(array,sizeof(array)/sizeof(array[0]));
53 print(array,sizeof(array)/sizeof(array[0]));
54 return;
55 }
运行结果:
二、选择排序
时间复杂度:o(n^2) 空间复杂度: o(1) 不稳定
算法:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
17 void option_sort(int array[],int64_t size){
18 if(size<=1){
19 return;
20 }
21 //[0,bound):有序序列 [bound,size):待排序序列
22 int64_t bound=0;
23 for(;bound<size;bound++){
24 int64_t cur=bound+1;
25 for(;cur<size;cur++){
26 if(array[cur]<array[bound]){ //类似于打擂台,谁小谁就放到bound的位置
27 swap(&array[cur],&array[bound]);
28 }//end if
29 }//end for
30 }//end for
31 return;
32 }
结果:
三、插入排序
时间复杂度:最坏:o(n^2) 最优:o(n) 空间复杂度:o(1) 稳定排序 此排序适合于数据较短或者差不多已经有序的数据
算法:
将一个数据插入到已经有序的数据中,从而得到一个新的有序序列 。这是将数据分为两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一元素(待插入的元素)。在第一部分排序完成后,再将最后这个元素插入到已经排好的第一部分中。
每步将一个待排序的记录,按其关键码值的大小插入到前面已经排序的数据的适当位置,知道全部插入完为止
代码~
12 void insert_sort(int array[],int64_t size)
13 {
14 if(size<=1){
15 return;
16 }
17 int64_t bound=1;
18 //有序序列:[0,bound) 待排序列:[bound,size)
19 for(;bound<size;bound++){
20 int64_t bound_value=array[bound];//由于后面会覆盖,使用变量将其保存
21 int64_t i=bound;
22 for(;i>0;--i){
23 if(bound_value<array[i-1]){ //若待排的数据小于前面数据,则将前
面的值给它
24 array[i]=array[i-1];
25 }
26 else{
27 break;
28 }
29 }//end for
30 //这里退出分为两种:
31 //1. 由于已经到了数组的第一个元素,那么此时数组的第一个元素就是待排数据的位置
32 //2. 找到了一个合适的位置,比它前面的数据大,那么此时这个位置就是它的位置
33 array[i]=bound_value;
34 }//end for
35 return;
36 }
四、堆排序
时间复杂度:建堆nlog(n)+删除o(logn)= o(logn) 空间复杂度o(1) 不稳定
算法:
堆排序利用了大根堆(或小根堆)堆顶记录的 关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
1) 建堆
2) 循环删除堆顶元素(用堆顶元素和最后一个元素进行交换)
13 void swap(int *a,int *b);
14 void adjustdown(int array[],int64_t size,int a)
15 {
10 }
11 printf("\n");
12 }
13 void swap(int *a,int *b);
14 void adjustdown(int array[],int64_t size,int a)
15 {
16 int parent=a;
17 int child=2*parent+1;//左孩子
18 //保证不是叶子结点
19 while(child<size){
20 //若右孩子的值大于右孩子,那么最大的孩子就为右孩子子
21 if((child+1)<size && array[child+1]>array[child] )
22 {
23 child=child+1;
24 }
25 //若双亲结点的值大于最大的孩子,那么满足堆的性质
26 if(array[parent]> array[child]){
27 return;
28 }
29 //不满足则进行交换,完成之后在进行向下调整
30 swap(&array[parent],&array[child]);
31 parent=child;
32 child=2*parent+1;
33 }
34 return;
35 }
36 void heapcreat(int array[],int64_t size)
37 {
38 if(size<=1){
39 return;
40 }
41 int i;
42 //size-1:最后一个节点 那么它的双亲节点就为size-1-1/2
43 for(i=(size-1-1)/2;i>=0;i--)
44 {
45 adjustdown(array,size,i);//采用向下调整的方法进行建堆
46 // 因为在后面我们在删除堆顶用向下调整
47 // 因此 这里我们就直接采用向下调整,当然也可以采用向上调整
48 }
49 }
50 void swap(int *a,int *b){
51 int tmp=*a;
52 *a=*b;
53 *b=tmp;
54 }
55 void popHeap(int array[],int size){
56 swap(&array[0],&array[size-1]);//交换堆顶和最后一个元素
57 adjustdown(array,size-1,0); // 此时可能不在满足堆的性质,进行向下调整
58 }
59 void heap_sort(int array[],int64_t size)
60 {
61 if(size<=1){
62 return;
63 }
64 //1、 创建堆 这里选择建大堆
65 heapcreat(array,size);
66 // 2、替换
67 int j;
68 for(j=0;j<size;j++){
69 popHeap(array,size-j);//删除堆顶元素
70
71 // swap(&array[0],&array[size-1-j]);
72 // adjustdown(array,size-1-j,0);
73 }
74 }
五、希尔排序
时间复杂度:o(n^2) 最优o(n^1.3) 空间复杂度:o(1) 不稳定
算法:
是插入排序的一种改进版本,它是先分组,在插入。
13 void shell_sort(int array[],int64_t size){
14 if(size<=1){
15 return;
16 }
17 //先插入第一组的第一个元素
18 //再插入第二组的第一个元素
19 //再插入第三组的第一个元素
20 //....
21
22 int64_t gap=size/2; //0,gap,2gap,3gap
23 for(;gap>=1;gap/=2){ //控制步长
24 int64_t bound=gap;
25 for(;bound<size;++bound){
26 int bound_value=array[bound];
27 int i=bound;// 其实在希尔排序这里,我们可以想象bound就是插入排序
里面的1,
28 //因为它每次比较的是跨过gap个元素的一组数据
29 for(;i>=gap;i-=gap){
30 if(bound_value<array[i-gap]){
31 array[i]=array[i-gap];
32 }
33 else{
34 break;
35 }
36 }
37 array[i]=bound_value;
38 }//end for
39 }// end for
40 return;
41 }
六、归并排序
时间复杂度: O(NlogN)
空间复杂度: 对于数组来说, O(N)
稳定性: 稳定排序
//实现归并
void MergeArray(int array[], int left, int mid, int right, int *tmp){
int beg1 = left; //这四个变量是每次需要归并的两个区间[beg1,end1)[beg2,end2)
int end1 = mid;
int beg2 = mid;
int end2 = right;
int index = left; //申请空间变量的下标
while (beg1<end1 && beg2<end2){
if (array[beg1] <= array[beg2]){
//就把beg1 的数据放进去
tmp[index++] = array[beg1++];
}
else{
//就把beg2 的数据放进去
tmp[index++] = array[beg2++];
}
}
//把剩下的元素追加到tmp末尾
while (beg1<end1){
tmp[index++] = array[beg1++];
}
while (beg2<end2){
tmp[index++] = array[beg2++];
}
//将tmp里面的元素拷回原数组,这里要记着array+left和tmp+left
// memcpy(array + left, tmp + left, sizeof((array[0])*(right - left)));
}
void _Mergesort(int array[], int left, int right, int *tmp){
if (right - left>1){
int mid = left + ((right - left) >>1 );
//保证左右区间都有序,才可进行归并
_Mergesort(array, left, mid, tmp);//左闭右开
_Mergesort(array, mid, right, tmp);
MergeArray(array, left, mid, right, tmp);
memcpy(array + left, tmp + left, sizeof(array[0])*(right - left));
}
//数据只有一个,那么必然有序,直接返回
}
// 递归的层数 logN
// 总的递归的次数 2logN
void Mergesort(int array[], int size){
// 先创建好缓冲区, 以备后面使用, 缓冲区的大小需要和
// 原有的数组长度相同.
int *tmp = (int *)malloc(sizeof(int)*size);
// 下划线版本的函数辅助进行递归
// 第一个参数, 输入数组的起始位置.
// 第二个第三个参数表示当前进行归并排序的区间
// [0, size)
// 第四个参数表示用于归并的缓冲区起始位置
_Mergesort(array, 0, size, tmp);
free(tmp);
}
七、快速排序
算法:
是对冒泡的一种改进,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
将最后一个设为基准值:
19 int64_t partion(int array[],int64_t begin,int64_t end){
20 //定义两个下标
21 int64_t left=begin;
22 int64_t right=end-1;
23 int tmp=array[end-1];//将最后一个设为基准值
24 while(left<right){
25 while(left<right && array[left] <= tmp){//第一个下标从前往后遍历,找比基本值大的元素
26 ++left;
27 }
28 while(left<right && array[right] >= tmp){//第二个下标从后往前遍历,找比基本值小的元素
29 --right;
30 }
31 if(left<right){
32 swap(&array[left],&array[right]);//找到之后两个交换位置
33 }
34 } 35 //left和right重合之后,就把基准值和left指向的元素进项交换
36 swap(&array[left],&array[end-1]);
37 //前面的都比基本值小,后面的都比基本值大
38 return left;
39 }
40
41 void _quickSort(int array[],int64_t begin,int64_t end){
42 if(end-begin<=1){
43 return;
44 }
45 int64_t mid=partion(array,begin,end);//借助此函数完成基准值选择及交换,返回基准值最终的位置
46 //基准值的左边都比他小右边都比他大
47 _quickSort(array,begin, mid);//递归处理左边
48 _quickSort(array,mid+1, end);//递归处理右边
49
50 }
51 void quick_sort(int array[],int64_t size){
52 if(size<=1){
53 return;
54 }
55 //下划线版本的函数辅助递归
56 _quickSort(array,0,size);
57 }
将第一个设为基准值:
22 //前面为基准
23 int64_t partion3(int array[],int64_t begin,int64_t end){
24 int out_put=begin;
25 int64_t tmp=array[out_put]i;//保存基准的值
26 int64_t left=begin+1;//从基准的下一个位置开始
27 int64_t right=end-1;
28
29 while(left<right){
30 //从左向右,若小于基准,则一直—++
31 while(left<right && array[left]<tmp){
32 left++;
33 }
34 //找到一个比基准大的值,就和right进行交换,right--
35 swap(&array[left],&array[right]);
36 right--;
37 }
38 //判断left的值若大于基准,则left--肯定小于基准
39 if(array[left]>tmp){
40 left--;}
41 //交换两个的值
42 swap(&array[left],&array[out_put]);
43 return left;
44 }
快排----挖坑法:
代码实现:
19 int64_t partion2(int array[],int64_t begin,int64_t end){
20 int64_t left=begin;
21 int64_t right=end-1;
22 int tmp=array[end-1];//将最后一个设为基准值
23 while(left<right){
23 while(left<right){
24 //若没有越界,且left小于基准,就一直++left,循环退出,找到一个比基准大的数
25 while(left<right && array[left]<=tmp){
26 left++;
27 }
28 //将其值付给right
29 if(left<right){
30 array[right]=array[left];
31 right--;
32 }
33 //若没有越界,且right大于基准,就一直--right,循环退出,找到一个比基准小的数
34 while(left<right && array[right]>=tmp){
35 right--;
36 }
37 //将其值付给left
38 if(left<right){
39 array[left]=array[right];
40 left++;
41 }
42 }
43 array[left]=tmp;//将基准值给left
44 return left;
45 }
快排----前后指针法
19 //前后指针法
20 int64_t partion4(int array[],int64_t begin,int64_t end){
21 int cur=begin;
22 int pre=cur-1;
23 int tmp=array[end-1];
24 while(cur<end){
25 //若cur值小于基准,先让pre++,在判断pre是否和cur相遇,没相遇,就交换,在cur++,相遇,直接cur++
26 if(array[cur]<tmp && ++pre!=cur){
27 swap(&array[cur],&array[pre]);
28 }
29 cur++;
30 }
31 //pre此时还指向最后一个比基准值小的数,让pre++指向比基准值大的数,若pre没有越界,就让pre和基准值交换
32 if(++pre!=end){
33 swap(&array[pre],&array[end-1]);
34 }
35 return pre;
36 }