1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 typedef struct Node{ 5 int num[10]; 6 int length; 7 char s[10]; 8 }pNode; 9 10 //exchange 11 void swap(pNode *L,int m,int n) 12 { 13 int temp=0; 14 temp=L->num[m]; 15 L->num[m]=L->num[n]; 16 L->num[n]=temp; 17 } 18 //bubble sort 19 void Bubblesort(pNode *L) 20 { 21 int i=1; 22 int j=1; 23 for(j=1;j<=L->length;j++) 24 { 25 for(i=L->length-1;i>=j;i--) 26 { 27 if(L->num[i]>L->num[i+1]) 28 swap(L,i,i+1); 29 } 30 } 31 } 32 33 //simple selection sort 34 35 36 //straight insertion sort 37 38 39 //heap sort 40 void HeapSort(pNode *L) 41 { 42 int i=1; 43 //for(i=L->length/2;i>0;i--) //this way is ok!!!! 44 for(i=L->length;i>0;i--) 45 Heap_adjust(L,i,L->length); 46 for(i=L->length;i>1;i--) 47 // for(i=L->length/2;i>0;i--) 48 { 49 swap(L,1,i); 50 Heap_adjust(L,1,i-1); 51 } 52 } 53 //heap adjust 54 void Heap_adjust(pNode *L,int s,int m) 55 { 56 int temp=0; 57 int j=0; 58 temp=L->num[s]; 59 for(j=2*s;j<=m;j=j*2) 60 { 61 if(j<m&&(L->num[j]<L->num[j+1])) 62 ++j; 63 if(temp>L->num[j]) 64 break; 65 L->num[s]=L->num[j]; 66 s=j; 67 } 68 L->num[s]=temp; 69 } 70 71 //merging sort 72 void Mergesort(pNode *L) 73 { 74 Msort(L->num,L->num,1,L->length); 75 } 76 void Msort(int num1[],int num2[],int m,int n) 77 { 78 int flag; 79 int num3[11]; 80 if(m==n) 81 { 82 num2[m]=num1[m]; //this is the recursion back condition 83 } 84 else 85 { 86 flag=(m+n)/2; 87 Msort(num1,num3,m,flag); 88 Msort(num1,num3,flag+1,n); 89 Merge(num3,num2,m,flag,n); 90 } 91 } 92 void Merge(int num1[],int num2[],int m,int flag,int n) 93 { 94 int j,k,l; 95 for(j=flag+1,k=m;m<=flag&&j<=n;k++) 96 { 97 if(num1[m]<num1[j]) 98 { 99 num2[k]=num1[m++]; 100 } 101 else 102 { 103 num2[k]=num1[j++]; 104 } 105 } 106 if(m<=flag) 107 { 108 for(l=0;l<=flag-m;l++) 109 num2[k+l]=num1[m+l]; 110 } 111 if(j<=n) 112 { 113 for(l=0;l<=n-j;l++) 114 num2[k+l]=num1[j+l]; 115 } 116 } 117 //quick sort 118 void Qsort(pNode *L,int low,int high) 119 { 120 int flag; 121 if(low<high) 122 { 123 flag=Partition(L,low,high); 124 Qsort(L,low,flag-1); 125 Qsort(L,low+1,high); 126 } 127 } 128 int Partition(pNode *L,int low,int high) 129 { 130 int flag; 131 flag=L->num[low]; 132 while(low<high) 133 { 134 while(low<high&&L->num[high]>=flag) 135 { 136 high--; 137 } 138 swap(L,low,high); 139 while(low<high&&L->num[low]<=flag) 140 { 141 low++; 142 } 143 swap(L,low,high); 144 } 145 return low; 146 } 147 //print 148 void Print(pNode *L) 149 { 150 int i=1; 151 for(;i<=L->length;i++) 152 { 153 printf("data:%d ",L->num[i]); 154 } 155 } 156 int main() 157 { 158 pNode *L=(pNode*)malloc(sizeof(struct Node)); 159 int num0[9]={50,10,90,30,70,40,80,60,20}; 160 L->length=9; 161 int i=0; 162 for(;i<9;i++) 163 { 164 L->num[i+1]=num0[i]; 165 } 166 167 //merge sort 168 //Mergesort(L); 169 170 //heap sort 171 //HeapSort(L); 172 173 //quick sort 174 //Qsort(L,1,9); 175 176 //bubble sort 177 Bubblesort(L); 178 Print(L); 179 180 return 0; 181 }
主要实现了冒泡排序、堆排序、归并排序、快速排序。其中归并排序、快速排序运用了递归算法。
冒泡排序:主要是用了双重循环,第一层循环控制整个排序的步长,第层循环在大于第一层循环的基础上从顺序表末端依次比较,将最小或者最大的数排到当前起一层循环的开始处。
堆排序
堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆,或者每个节点的值都小于或等于其左右孩子的值,称为小顶堆。
步骤1、将数据建立成一个大顶堆,利用data(i)>=data(2i)、data(i)>=data(2i+1)主要就是要保证每个节点要大于其左右孩子
步骤2、根据步骤1建立的大顶堆,我们可以知道该完全二叉树的顶堆是最大值,那么我们把最大值换到这颗树的最底端,这样就完成了将最大的数放在最后面的一次交换,我们把最底端的数换到最顶端就会出现不符合大顶堆数据结构的情况于是我们需要调整。如代码:
53 //heap adjust
54 void Heap_adjust(pNode *L,int s,int m)
55 {
56 int temp=0;
57 int j=0;
58 temp=L->num[s];
59 for(j=2*s;j<=m;j=j*2)
60 {
61 if(j<m&&(L->num[j]<L->num[j+1]))
62 ++j;
63 if(temp>L->num[j])
64 break;
65 L->num[s]=L->num[j];
66 s=j;
67 }
68 L->num[s]=temp;
69 }
我们对Heap_adjust(pNode *L,int s,int m)函数传递的参数是Heap_adjust(L,1,i-1),相当于s=1,m=i-1,我们调整的范围就是:从我们刚刚交换数据的最顶端到最底端减1,即1<=x<i-1;
调整的目的就是保证目前的完全二叉树是一个大顶堆,最顶端的数一定是最大的,只有这样后面我们才可以不断地将顶端的数据交换到底端。调整的方法就是每次先找到节点然后在找到这个节点的左右孩子,所以变化是以2的倍数变化的,只有这样才符合data(i)>=data(2i)、data(i)>=data(2i+1)这个公式。
归并排序
归并排序:假设初始序列含有n个记录,则可以看成是n个有序的序列,每个子序列的长度为1,然后归并得到n/2个长度为2或者为1的有序子序列;再归并......直到得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
步骤1、将提供的数据分成两段,如我们提供的数据是50、10、90、30、70、40、80、60、20一共9个数字
第一次调用Msort(num1,num3,m,flag)函数,m=1,flag=9将1-9分成1-5、6-9两段,然后1-5这段数据继续迭代m=1,flag=5,将1-5分成1-3、4-5两段,然后1-3继续迭代m=1,flag=3,将1-3分成1-2,3-3两段,然后1-2继续迭代m=1,flag=2,将1-2分成1-1,2-2然后我们有一个递归返回的判断 num2[m]=num1[m],此时m=1,n=1递归返回第一次,程序执行1-2范围内1-1的另一半2-2,m=n递归返回第二次,然后在将这两个数调用Merge()函数做一个归并,第一次归并,至此1-2分成的两段就完成了归并。
然后程序就继续往下执行就是函数从1-3范围的另一半3-3,因为m=n所以递归返回,直接执行1-2,3-3的归并,这样1-3就完成了归并。
递归继续返回应该是1-5范围的另一半4-5进行递归调用,因为1-3我们已经完成了归并,m=4,n=5将4-5分成4-4,5-5,即数据段的两段都直接递归返回,然后将s=4,s=5分别存入num2数组中:num2[m]=num1[m]; //this is the recursion back condition,这样num2[4]、num2[5]都存放了值,然后程序已经执行了87行、88行代码,所以程序继续执行89行代码,也就是对num2[4]、num2[5]进行一个归并,然后递归返回将1-3,4-5这两段数据调用89行代码做一个归并,至此1-9分成的两段1-5,6-9前半段已经完成了排序,程序也已经递归返回到了最开始执行Msort函数时的状态,即第一调用Msort函数分成的1-5,6-9段这儿,相当于前面的所有步骤在第一次进入Msort函数后将1-9分成了1-5,6-9两段,然后m=1,flag=5调用87行代码,这个递归调用返回后继续执行88行代码。
后面程序继续执行88行代码然后重复数据段1-5的步骤完成6-9的数据排序,直到88行代码递归返回了最后就只有1-5,6-9段的数据合并了那么执行89行代码完成最后的数据合并就完成了整个归并排序。可以看出整个过程是不断在分段、合并,最后程序递归返回到最初的分段然后合并两个长的数据段就完成了归并排序。所以在写程序时Merge函数时我们可以想象这已经是两个分好的大数据段了,我们需要对这两个数据段进行合并,这样对里面的变量变化范围就很好判断,而且思路顿时清醒很多,如果绕道递归里面去了将会十分的复杂,同时要注意这个Msor函数里面有两次递归调用,其执行顺序是一个调用一次Msort函数,会包含这个函数里面的所有内容,也就是说87行的函数每一次递归返回都会调用一次88、89行代码然后完成一次归并,因为在程序里面一次函数调用会形成一个栈帧,这个栈帧包含了函数的所有内容包括变量,函数的返回地址等等,所以Msort函数每递归返回一次,都会依次去执行后面的代码,也就是数据段的另一个范围,然后调用89行代码把这两个数据段完成合并。
87 Msort(num1,num3,m,flag); 88 Msort(num1,num3,flag+1,n); 89 Merge(num3,num2,m,flag,n);
快速排序
快速排序:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
快排的关键点就是不断地变化关键字的位置,也就是这个关键字左边的数小于关键字,右边的数大于关键字,不断变化这个关键字的位置最终完成排序。