冒泡排序:
重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到不再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,故名。
算法步骤:
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。对所有元素在一趟比较之后,最后的元素应该就是最大的数。
3.针对除最后已排好序的所有的元素重复以上步骤1和2,直到没有任何一对数字需要比较。
时间复杂度:
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数和记录移动次数均达到最小值:C(min) = n-1, M(min) = 0。所以,冒泡排序最好的时间复杂度为:o(n)。若初始文件是反序的,需要进行趟排序。每趟排序要进行次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:C(max) = n(n-1)/2 = o(n^2) M(max) = 3n(n-1)/2 = o(n^2)。所以,冒泡排序的最坏时间复杂度:o(n^2)
综上,因此冒泡排序总的平均时间复杂度为:o(n^2)。
空间复杂度:
该算法只有在进行数据交换时最多需要一个临时变量,因此空间复杂度为o(1)。
算法稳定性:
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,则不需要进行交换;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,因此相同元素的前后顺序不会发生改变,冒泡排序是一种稳定排序算法。
- #include <stdio.h>
- void Swap(int *a,int *b)
- {
- int tmp = *a;
- *a = *b;
- *b = tmp;
- }
- void BubbleSort(int arr[],int len)
- {
- /*需要n-1趟排序*/
- for (int i = 0; i < len - 1; ++i)
- {
- for (int j = 0; j < len -1 - i; ++j)
- {
- if (arr[j] > arr[j+1])
- {
- swap(&arr[j],&arr[j+1]);
- }
- }
- }
- }
- int main(int argc, char const *argv[])
- {
- int arr[10] = {9,8,7,6,5,4,3,2,1,0};
- int len = sizeof(arr)/sizeof(int);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ",arr[i]);
- }
- printf("\n");
- BubbleSort(arr,len);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- return 0;
- }
直接插入排序:
有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序。
算法步骤:
1.从第一个元素开始,该元素可以认为已经被排序。
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
5. 将新元素插入到下一位置中。
6. 重复步骤2。
时间复杂度:
如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。插入排序的赋值操作是比较操作的次数加上 (n-1)次。平均来说插入排序算法的时间复杂度为O(n^2)。因而,插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序还是一个不错的选择。
空间复杂度:
直接插入排序只有需要一个临时变量存储将要插入的数据,因此空间复杂度为o(1)。
算法稳定性:
直接插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有1个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
- #include <stdio.h>
- void InsertSort(int arr[],int n)
- {
- for (int i = 1; i < n; ++i)
- {
- int tmp = arr[i];
- int j;
- for (j = i; j > 0 && arr[j-1] > tmp ; --j)
- {
- arr[j] = arr[j-1];
- }
- arr[j] = tmp;
- }
- }
- int main(int argc, char const *argv[])
- {
- int arr[10] = {9,8,7,6,5,4,3,2,1,0};
- int len = sizeof(arr)/sizeof(int);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- InsertSort(arr,len);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- return 0;
- }
希尔排序:
希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。
算法步骤:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
时间复杂度:
希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n^2),而Hibbard增量的希尔排序的时间复杂度为O(N^(5/4)),但是现今仍然没有人能找出希尔排序的精确下界。
空间复杂度:
希尔排序只有需要一个临时变量存储将要插入的数据,因此空间复杂度为o(1)。
算法稳定性:
由于进行了多次直接插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以希尔排序是不稳定的。
- #include <stdio.h>
- void ShellSort1(int arr[],int n)
- {
- /*步长为gap,每次排序后gap减半,知道gap =1 */
- for (int gap = n/2; gap > 0; gap /= 2)
- {
- /*对各组进行排序*/
- for (int i = gap; i < n; ++i)
- {
- int j;
- int tmp = arr[i];
- for (j = i; j >= gap && arr[j - gap] > tmp; j -= gap)
- {
- arr[j] = arr[j - gap];
- }
- arr[j] = tmp;
- }
- }
- }
- void ShellSort2(int arr[],int n)
- {
- /*步长为gap,每次排序后gap减半,知道gap =1 */
- for (int gap = n/2; gap > 0; gap /= 2)
- {
- /*对各组进行排序*/
- for (int i = gap; i < n; ++i)
- {
- int j;
- int tmp = arr[i];
- for (j = i; j >= gap && arr[j -gap] > tmp; j -= gap)
- {
- arr[j] = arr[j - gap];
- arr[j -gap] = tmp ;
- }
- }
- }
- }
- int main(int argc, char const *argv[])
- {
- int arr[10] = {9,8,7,6,5,4,3,2,1,0};
- int len = sizeof(arr)/sizeof(int);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- ShellSort1(arr,len);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- return 0;
- }
简单选择排序:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
算法步骤:
n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果:
1.初始状态:无序区为R[1..n],有序区为空。
2.第1趟排序在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
3.第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。
这样,n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果。
时间复杂度:
空间复杂度:
算法稳定性:
- #include <stdio.h>
- void swap(int *a,int *b)
- {
- int tmp = *a;
- *a = *b;
- *b = tmp;
- }
- void SimpleSelectSort(int arr[],int len)
- {
- for (int i = 0; i < len; ++i)
- {
- int min_index = i;
- for (int j = i + 1; j < len ; ++j)
- {
- if (arr[j] < arr[min_index])
- {
- min_index = j;
- }
- }
- if (min_index != i)
- {
- swap(&arr[i],&arr[min_index]);
- }
- }
- }
- int main(int argc, char const *argv[])
- {
- int arr[10] = {9,8,7,6,5,4,3,2,1,0};
- int len = sizeof(arr)/sizeof(int);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- SimpleSelectSort(arr,len);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ", arr[i]);
- }
- printf("\n");
- return 0;
- }
堆排序:
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。
算法步骤:
时间复杂度:
空间复杂度:
算法稳定性:
- #include <stdio.h>
- void swap(int *a,int *b)
- {
- int tmp = *a;
- *a = *b;
- *b = tmp;
- }
- /*堆调整*/
- void HeapAdjust(int *arr,int i,int size){
- int lchild = 2*i+1; //节点i的左子节点
- int rchild = 2*(i+1); //节点i的右子节点
- int max = i;
- if (i <= size/2) //只对非叶节点进行调整
- {
- if (lchild < size && arr[lchild] > arr[max])
- {
- max = lchild;
- }
- if (rchild < size && arr[rchild] > arr[max])
- {
- max = rchild;
- }
- if (max != i)
- {
- swap(&arr[i],&arr[max]);
- HeapAdjust(arr,max,size);//对调整过的max节点重新进行堆调整
- }
- }
- }
- /*建立无序大顶堆*/
- void BulidHeap(int *arr,int size)
- {
- for (int i = size/2; i >= 0; --i)
- {
- HeapAdjust(arr,i,size);
- }
- }
- /*堆排序*/
- void HeapSort(int *arr, int size)
- {
- BulidHeap(arr,size);
- for (int i = size - 1; i > 0; --i)
- {
- swap(&arr[0], &arr[i]);
- HeapAdjust(arr,0,i);
- }
- }
- int main(int argc, char const *argv[])
- {
- int size = 10;
- int arr[10] = {9,8,7,6,5,4,3,2,1,0};
- for (int i = 0; i < size; ++i)
- {
- printf("%d ",arr[i]);
- }
- printf("\n");
- HeapSort(arr, size);
- for (int i = 0; i < size; ++i)
- {
- printf("%d ",arr[i]);
- }
- printf("\n");
- return 0;
- }
归并排序:
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列的排序算法。归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。它将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
算法步骤:
时间复杂度:
空间复杂度:
算法稳定性:
- #include <stdio.h>
- int arr[10] = {9,8,7,6,5,4,3,2,1,0};
- int tmp[10];
- void Merge(int begin,int middle,int end)
- {
- int i = begin;
- int j = middle + 1;
- int k = begin;
- while(i <= middle && j <= end)
- {
- if (arr[i] <= arr[j] )
- {
- tmp[k++] = arr[i++];
- }
- else
- {
- tmp[k++] = arr[j++];
- }
- }
- while(i <= middle)
- {
- tmp[k++] = arr[i++];
- }
- while(j <= end)
- {
- tmp[k++] = arr[j++];
- }
- for (k = begin; k <= end; ++k)
- {
- arr[k] = tmp[k];
- }
- }
- void MergeSort(int begin,int end)
- {
- if (begin < end)
- {
- int middle = (begin + end) / 2;
- MergeSort(begin,middle);
- MergeSort(middle + 1,end);
- Merge(begin,middle,end);
- }
- }
- int main(int argc, char const *argv[])
- {
- int len = sizeof(arr)/sizeof(int);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ",arr[i] );
- }
- printf("\n");
- MergeSort(0,len - 1);
- for (int i = 0; i < len; ++i)
- {
- printf("%d ",arr[i] );
- }
- printf("\n");
- return 0;
- }
快速排序:
快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法步骤:
A[0]
|
A[1]
|
A[2]
|
A[3]
|
A[4]
|
A[5]
|
A[6]
|
49
|
38
|
65
|
97
|
76
|
13
|
27
|
时间复杂度:
空间复杂度:
算法稳定性:
快速排序不稳定,且是在关键元素和某个元素发生交换时导致稳定性被破坏,比如序列5 3 3 4 3 8 9 10 11, 现在中枢元素5和3(第5个元素,下标从1开始计)交换就会破坏元素3的稳定性。
- #include <stdio.h>
- #include <string.h>
- #include <malloc.h>
- void swap(int *a,int *b)
- {
- int tmp;
- tmp = *a;
- *a = *b;
- *b = tmp;
- }
- int partition(int *arr,int low,int high)
- {
- int pivot = arr[low];
- while(low < high)
- {
- while(low < high && arr[high] >= pivot)
- {
- high--;
- }
- if (low < high)
- {
- swap(&arr[low],&arr[high]);
- low++;
- }
- while(low < high && arr[low] <= pivot)
- {
- low++;
- }
- if(low < high)
- {
- swap(&arr[low],&arr[high]);
- high--;
- }
- }
- return low;
- }
- void quickSort(int *arr,int low,int high)
- {
- int pivotpos;
- if (low < high)
- {
- pivotpos = partition(arr,low,high);
- quickSort(arr,low,pivotpos-1);
- quickSort(arr,pivotpos+1,high);
- }
- }
- int main(int argc, char const *argv[])
- {
- int n ;
- printf("please input the length of arr:\n");
- scanf("%d",&n);
- //int *arr = (int*)malloc(n * sizeof(int));
- int arr[n];
- printf("please input %d numbers for each elements\n", n);
- for(int i = 0; i < n; i++)
- {
- scanf("%d",&arr[i]);
- }
- quickSort(arr,0,n-1);
- for (int i = 0; i < n; ++i)
- {
- printf("%d ",arr[i]);
- }
- printf("\n");
- return 0;
- }
以上就是基本的数据结构排序算法,欢迎补充和讨论。