排序算法总结

冒泡排序(BubbleSort)

  • 基本思想:两个数比较大小,较大的数下沉,较小的数冒起来。

  • 过程:

    • 比较相邻的两个数据,如果第二个数小,就交换位置。
    • 从后向前两两比较,一直到比较最前两个数据。最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。
    • 继续重复上述过程,依次将第2.3...n-1个最小数排好位置。

    以下图示

    平均时间复杂度:O(n2)

代码实现

void Bubble_Sort(vector<int>& arr, int n) {
    if (n == 0)return;
    for (int i = 0; i < n - 1; ++i) {
        bool flag = true;//flag作为标记,如果仍是true的话说明已经排序了直接结束
        for (int j = n - 1; j > i; --j) {
            if (arr[j - 1] > arr[j]) {
                swap(arr[j - 1], arr[j]);
                flag = false;
            }
        }
        if (flag)return;
    }
}

插入排序(Insertion Sort)

基本思想:
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

插入排序很像放入扑克牌

过程:

代码实现:

void insertion_sort(int arr[], int len) {
	for (int i = 1; i < len; i++) {
		int key = arr[i];
		int j = i - 1;
		while ((j >= 0) && (key < arr[j])) {
			arr[j + 1] = arr[j];
			--j;
		}
		arr[j + 1] = key;
	}
}

从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将该元素移到下一位置
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
将新元素插入到该位置后

重复步骤2~5

可以采用二分查找法来减少“比较操作”的数目,而由于“交换操作”的数目不变,算法的时间复杂度依旧为\(O(n^2)\)。该算法可以认为是插入排序的一个变种,称为二分查找插入排序。

优化Code:

void HinsertSort(int nums[], int n) {
    int low, high, mid;
    for (int i = 0; i < n; ++i) {
        int tmp = nums[i];
        low = 0;
        high = i - 1;
        //采用折半查找法判断插入位置,最终变量 left表示插入位置
        while (low <= high) {
            mid = low + (low + high) >> 1;
            if (nums[mid] > tmp)
                high = mid - 1;
            else
                low = mid + 1;
        }
        //有序表中插入位置后的元素统一后移
        for (int j = i - 1; j >= high +1; --j) {
            nums[j + 1] = nums[j];
        }
        nums[high + 1] = tmp;//插入元素
    }
}

希尔排序(Shell Sort)

希尔排序(Shell Sort),也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

实现代码:

void ShellSort(vector<int>&nums, int n) {
    for (int dk = n / 2; dk >= 1; dk>>=1) {
        for (int i = dk; i < n; ++i) {
            if (nums[i] < nums[i - dk]) {
                int temp = nums[i], j;
                for (j = i - dk; j >= 0 && temp < nums[j]; j -= dk) {
                    nums[j + dk] = nums[j];
                }
                nums[j + dk] = temp;
            }
        }
    }
}

快速排序(Quicksort)

基本思想:(分治)

  • 先从数列中取出一个数作为key值(哨点)
  • 将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;
  • 对左右两个小数列重复第二步,直至各区间只有1个数。

辅助理解:挖坑填数

举个例子

如无序数组[6 2 4 1 5 9]

a),先把第一项[6]取出来,

用[6]依次与其余项进行比较,

如果比[6]小就放[6]前边,2 4 1 5都比[6]小,所以全部放到[6]前边

如果比[6]大就放[6]后边,9比[6]大,放到[6]后边,//6出列后大喝一声,比我小的站前边,比我大的站后边,行动吧!霸气十足~

一趟排完后变成下边这样:

排序前 6 2 4 1 5 9

排序后 2 4 1 5 6 9

b),对前半拉[2 4 1 5]继续进行快速排序

重复步骤a)后变成下边这样:

排序前 2 4 1 5

排序后 1 2 4 5

前半拉排序完成,总的排序也完成:

排序前:[6 2 4 1 5 9]

排序后:[1 2 4 5 6 9]

排序结束

实现代码:

void quicksort(vector<int>& a,int left, int right)
{
	int i, j, t;
	if (left > right)
		return;//判断是否只剩下1个数字或者输入错误

	t = a[left]; //t存储基准数
	i = left;
	j = right;
	while (i != j)
	{
		//顺序很重要,要先右边开始找
		while (a[j] >= t && i < j)
            j--;
		while (a[i] <= t && i < j)
			i++;
		if (i < j)//进行i,j两个哨兵对应值的交换
			swap(a[i],a[j])
	}
	//最终将基准数归位
	a[left] = a[i];
	a[i] = t;

	quicksort(left, i - 1);//继续处理左边的,利用递归
	quicksort(i + 1, right);//继续处理右边的,利用递归
}

平均时间复杂度:\(O(NlogN)\)

堆排序(HeapSort)

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。

实现代码:

#include<iostream>
using namespace std;

int h[101];//存储堆得数组
int n;//堆得大小

void swap(int x, int y) {
	int t = h[x];
	h[x] = h[y];
	h[y] = t;
}

void sifdown(int i){ //传入一个需要向下调整得节点编号,这里传1,即从堆顶向下调整
	int t;
	bool flag = false;//flag标记是否需要往下调整

	while (i * 2 <= n && flag == false) {
		//先判断它和左儿子得关系,并t记录较大节点编号
		if (h[i] < h[i * 2])
			t = i * 2;
		else
			t = i;
		//如果它有右儿子
		if(i*2+1<=n)
			if (h[t] < h[2 * i + 1])
				t = i * 2 + 1;

		//如果发现最大的节点编号不是自己说明子节点中有比父节点更大的
		if (t != i) {
			swap(t, i);//交换它们
			i = t;//更新i为刚刚与它交换的儿子编号,编于下面向下调整
		}
		else
			flag = true;//否则说明当前的父节点以及比两个子节点都大了,不用调整
	}
	
}

void creat_heap() {//建堆函数
	int i;
	for (i = n / 2; i >= 1; i--)
		sifdown(i);
}

void heapsort() {
	while (n > 1) {
		swap(1, n);
		n--;
		sifdown(1);
	}
} 

int main() {
	int i, num;
	cin >> num;

	for (int i = 1; i <= num; ++i)
		cin >> h[i];
	n = num;
	creat_heap();

	heapsort();

	for (int i = 1; i <= num; ++i)
		cout << i << " ";

	cout << endl;
	return 0;
}

基数排序(RadixSort)

  • 基本思想:
    BinSort想法非常简单,首先创建数组A[MaxValue];然后将每个数放到相应的位置上(例如17放在下标17的数组位置);最后遍历数组,即为排序后的结果。
  • 图示:

BinSort

  • 问题: 当序列中存在较大值时,BinSort 的排序方法会浪费大量的空间开销。
RadixSort
  • 基本思想: 基数排序是在BinSort的基础上,通过基数的限制来减少空间的开销。
posted @ 2020-04-01 11:13  RioTian  阅读(33)  评论(0编辑  收藏  举报