几种常见的排序算法

Algorithms Lesson 1: Bubblesort

 

代码

for i = (n-1)to 1 for j = 0 to (i-1) if A[j] > A[j+1] swap (A[j], A[j+1])

 

复杂度分析

n 个元素, 比较次数有 (n-1) + (n-2) + ...... + 1 = n(n-1)/2,

O(n2)

 

Algorithms Lesson 2: Insertion Sort

代码

for i = 1 to n - 1 j = i while j > 0 and A[j] < A[j-1] swap (A[j], A[j-1]) j = j - 1

换个形式

void InsertSort(char array[],unsigned int n) { int i,j; int temp; for(i=1;i0 && temp < array[j-1] ; j--)//compare the new array with temp(maybe -1?) { array[j]=array[j-1];//all larger elements are moved one pot to the right array[j-1]=temp; } } }

算法复杂度

Algorithms Lesson 3: Merge Sort

代码

Algorithms Lesson 4:Selection sort

代码

// selection sort function module in C void selectionSort(int data[], int count) { int i, j, min, temp; for (i = 0; i < count - 1; i++) { /* find the minimum */ min = i; for (j = i+1; j < count; j++) { if (data[j] < data[min]) { min = j; } } /* swap data[i] and data[min] */ temp = data[i]; data[i] = data[min]; data[min] = temp; } }

复杂度分析

选择排序的交换操作介于0(n − 1)次之间。选择排序的比较操作n(n − 1) / 2次之间。选择排序的赋值操作介于03(n − 1)次之间。

比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。 交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。 交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。

 

Algorithms Lesson 5:Merge sort

算法描述

代码

以下示例代码实现了归并操作。array[]是元素序列,其中从索引p开始到q位置,按照升序排列,同时,从(q+1)到r也已经按照升序排列,merge()函数将把这两个已经排序好的子序列合并成一个排序序列。结果放到array中。 static void merge(int array[], int low, int mid, int high) { int i, k; int *temp = (int *) malloc((high-low+1) * sizeof(int)); //申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 int begin1 = low; int end1 = mid; int begin2 = mid + 1; int end2 = high; for (k = 0; begin1 <= end1 && begin2 <= end2; ++k) //比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 if(array[begin1]

算法复杂度

比较操作的次数介于(nlogn) / 2nlognn + 1赋值操作的次数是(2nlogn)。 归并算法的空间复杂度为:Θ (n)

 

 

 

 

Algorithms Lesson 6:Quick sort

算法分析

快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

步骤为:

  1. 从数列中挑出一个元素,称为 "基准"(pivot),
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割之后,该基准是它的最后位置。这个称为分割(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递回的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递回下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

代码

排序一个整数的阵列 void swap(int *a, int *b) { int t=*a; *a=*b; *b=t; } void quicksort(int arr[],int beg,int end) { if (end >= beg + 1) { int piv = arr[beg], k = beg + 1, r = end; while (k < r) { if (arr[k] < piv) k++; else swap(&arr[k], &arr[r--]); } if (arr[k] < piv){ swap(&arr[k],&arr[beg]); quicksort(arr, beg, k); quicksort(arr, r, end); }else { if (end - beg == 1) return; swap(&arr[--k],&arr[beg]); quicksort(arr, beg, k); quicksort(arr, r, end); } } }

Algorithms Lesson 7:Heapsort

算法分析

堆积树节点的访问

代码

#include #include #include const int HEAP_SIZE = 13; //堆積樹大小 int parent(int); int left(int); int right(int); void Max_Heapify(int [], int, int); void Build_Max_Heap(int []); void print(int []); void HeapSort(int [], int); /*父結點*/ int parent(int i) { return (int)floor(i / 2); } /*左子結點*/ int left(int i) { return 2 * i; } /*右子結點*/ int right(int i) { return (2 * i + 1); } /*單一子結點最大堆積樹調整*/ void Max_Heapify(int A[], int i, int heap_size) { int l = left(i); int r = right(i); int largest; int temp; if(l < heap_size && A[l] > A[i]) { largest = l; } else { largest = i; } if(r < heap_size && A[r] > A[largest]) { largest = r; } if(largest != i) { temp = A[i]; A[i] = A[largest]; A[largest] = temp; Max_Heapify(A, largest, heap_size); } } /*建立最大堆積樹*/ void Build_Max_Heap(int A[]) { for(int i = HEAP_SIZE; i >= 0; i--) { Max_Heapify(A, i, HEAP_SIZE); } } /*印出樹狀結構*/ void print(int A[]) { for(int i = 0; i < HEAP_SIZE;i++) { printf("%d ", A[i]); } printf("/n"); } /*堆積排序程序碼*/ void HeapSort(int A[], int heap_size) { int temp; Build_Max_Heap(A); for(int i = heap_size - 1; i >= 1; i--) { temp = A[0]; A[0] = A[i]; A[i] = temp; heap_size = heap_size - 1; Max_Heapify(A, 0, heap_size); } print(A); } /*輸入資料並做堆積排序*/ int main(void) { int A[HEAP_SIZE] = {19, 1, 10, 14, 16, 4, 7, 9, 3, 2, 8, 5, 11}; HeapSort(A, HEAP_SIZE); system("pause"); return 0; }

通常堆积树(heap)是通过一维数组来实现的。在起始阵列为 0 的情形中:

  • 堆积树的根节点(即堆积树的最大值)存放在阵列位置 1 的地方;

  注意:不使用位置 0,否则左子树永远为 0[2]

  • 节点i的左子节点在位置 (2*i);
  • 节点i的右子节点在位置 (2*i+1);
  • 节点i的父节点在位置 floor(i/2);

 

堆积树的操作

在堆积树的数据结构中,堆积树中的最大值总是位于根节点。堆积树中定义以下几种操作:

  • 最大堆积调整(Max_Heapify):将堆积树的末端子结点作调整,使得子结点永远小于父结点
  • 建立最大堆积(Build_Max_Heap):将堆积树所有数据重新排序
  • 堆积排序(HeapSort):移除位在第一个数据的根结点,并做最大堆积调整的递归运算

归并操作的工作原理如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针达到序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。插入排序的赋值操作是比较操作的次数减去(n-1)次。平均来说插入排序算法复杂度为O(n2)。因而,插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序还是一个不错的选择。

你可能感兴趣的:(数据结构)