几个简单的排序算法及其优化
排序算法是数据结构、算法中的基本组成部分,TAOCP中专门有一卷是讲排序算法的。网上有个排序算法的介绍和博客很多,可以自行谷歌百度之。相关基本介绍可以查看Wiki。
这里我们简单介绍几个简单的排序算法:冒泡排序、选择排序、插入排序、快速排序。其中,我们分别对冒泡排序、选择排序、插入排序做了小小的优化,快速排序算法我们采用的分割策略是《快速排序的三种分割策略》中介绍的第三种分割策略。
冒泡排序的原理是始终比较两个相邻的元素,如果符合条件就交换。其改进方法是,添加一个标示符,初始的时候用来表示待排序序列时已经有序的,如果存在交换操作,则说明原来序列不是有序的,修改该标示符。如果没有修改该标示符,则表示待排序序列时有序的,不再需要继续无谓的操作,终止循环。
选择排序内循环中每次一直与一个元素进行比较,如果符合条件就交换。这是它和冒泡排序最大的差别。由于只要符合条件就交换,但是这种交换未必就是必须的,依次外循环之后只需要将待排序序列中最小或最大的元素选出来即可,所以可以用个辅助索引记录最小元素的索引,用一个辅助值记录最小的元素,当内循环指向完毕后,检测索引是否变化或者最小元素值是否与当前元素不同,索引值的变动和最小元素值的变动是一致的,如果变动,则进行交换即可。这样可以省去不必要的交换操作。不过,用来记录最小元素的值是没有必要的,因为可以根据索引直接指导最小元素的值。
插入排序是将后面的元素依次插入到前面已经排好序的序列中。其方法是从后面的元素向前依次检测相邻的两个元素是否符合条件,如果符合则交换。这样同样也造成不必要的交换操作,改进的方法是:填坑。先记录待插入的元素,将这个元素的位置视为一个坑,依次比较前面的元素和记录的待插入元素的关系,如果符合条件就填坑,填完坑前面的元素又变成了坑,依次检察下去,直至检测到不符合填坑条件位置,这时就可以终止循环,没必要继续向前检测了,因为前面的序列都是有序的,前面的元素必定更不符合填坑条件。
有关快速排序的介绍可以查看《快速排序专辑》中的相关博文。
#include <stdio.h> // 冒泡排序 void BubbleSort(int arr[], int n) { int i = 0, j = 0, temp = 0; for (i = 0; i < n - 1; ++i) { for (j = 0; j < n - 1 - i; ++j) { if (arr[j] > arr[j + 1]) // 始终比较两个相邻的元素,如果符合条件就交换 { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } // 改进的冒泡排序 void BubbleSort2(int arr[], int n) { int i = 0, j = 0, temp = 0; int flag = 1; // 标示序列需要排序 for (i = 0; i < n - 1 && flag == 1 /* 如果无序则继续循环,如果有序了,则终止循环 */; ++i) { flag = 0; // 假设序列有序 for (j = 0; j < n - 1 - i; ++j) { if (arr[j] > arr[j + 1]) // 比较两个相邻的元素 { flag = 1; // 假设失败 temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } // 选择排序 void SelectionSort(int arr[], int n) { int i = 0, j = 0, temp = 0; for (i = 0; i < n - 1; ++i) { for (j = i + 1; j < n; ++j) { if (arr[i] > arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } // 改进的选择排序 void SelectionSort2(int arr[], int n) { int i = 0, j = 0, temp = 0; int idx = 0; // 用于记录最小的索引 //int min = 0; // 用来记录最小的元素值 //min是多余的,因为记录的idx,min自然可以用idx来表示:arr[idx] for (i = 0; i < n - 1; ++i) { idx = i; for (j = i + 1; j < n; ++j) { if (arr[j] < arr[idx]) { idx = j; //min = arr[j]; } } if (idx != i) // 如果idx有变动,交换,也可以不检测,直接交换 //if (min != arr[i]) { temp = arr[i]; arr[i] = arr[idx]; arr[idx] = temp; } } } // 插入排序 void InsertSort(int arr[], int n) { int i = 0, j = 0; int temp; for (i = 1; i < n; ++i) { for (j = i - 1; j >= 0; --j) { if (arr[j] > arr[j + 1]) { // 前后交换 temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } // 改进的插入排序 void InsertSort2(int arr[], int n) { int i = 0, j = 0; int temp = 0; // 用于记录尾元素 for (i = 1; i < n; ++i) { temp = arr[i]; // 把大于temp的元素向后移 // 定位 for (j = i - 1; j >= 0 && arr[j] > temp /* 如果符合不符合条件就终止循环,不再检测之前的元素 */; --j) { arr[j + 1] = arr[j]; } arr[j + 1] = temp; } } // 快速排序 int Partition(int arr[], int p, int r) { int x = 0, i = 0, j = 0, temp = 0; x = arr[p]; i = p - 1; j = r + 1; while (i < j) { while (arr[--j] > x); while (arr[++i] > x); if (i < j) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } return j; } void QSort(int arr[], int left, int right) { int q = 0; if (left < right) { q = Partition(arr, left, right); QSort(arr, left, q); QSort(arr, q + 1, right); } } // 封装 void QuickSort(int arr[], int n) { QSort(arr, 0, n - 1); } int main() { return 0; }