快速排序和其它的六种排序算法小总结写给自己看的

一. 选择排序   O(n^2)   不稳定

我的思路,但不是算法实现思路

这个排序,顾名思义,选择,选择,

第一步:从n个数中选择最小的那个,放到第0个位置*
第二步:再从 第1个数 到 最后一个数中,选择最小的,放到第1个位置



第N步:再从 第n-1个数 到 最后一个数中,选择最小的,放到第n-1个位置

template<typename T>
void selectionSort(T arr[], int n) {
	for (int i = 0; i < n; i++) {
		int minIndex=i;                       //将当前位置设置为最小索引
		for (int j = i + 1; j < n; j++) {
			if (arr[j] < arr[minIndex])
				minIndex = j;                 //遍历第i个数之后的数,找出最小的数的索引 
		}
		swap(arr[i], arr[minIndex]);  //将当前的数和最小的数进行交换
	//这里用的是c++ swap()是std里自带的
	//建议自己写一个,因为c++自带的swap()比自己写的复杂,所以运行时间慢一点。	                              
	}
}

个人感觉:自己写的出来就好,没什么卵用

二. 冒泡排序   O(n^2)   稳定

我的思路

有n个泡泡,将它们随机变成串成一串冰糖葫芦
从上到下,并给它们标上序号,序号大小,从上依次递减
0…n-1
我们选择最后一个泡泡,将它与它前一个泡泡比较大小,如果小,
那么,就将两个泡泡对换.
直到当前的泡泡比前面的泡泡大。
抛弃当前的泡泡。
换前一个泡泡当主角。
在循环上一操作。
唉。表述的太烂了。

template<typename T>
void bubbleSort(T arr[], int n) {

	int newn; // 使用newn进行优化

	do {
		newn = 0;
		for (int i = 1; i < n; i++)
			if (arr[i - 1] > arr[i]) {
				swap(arr[i - 1], arr[i]);

				// 记录最后一次的交换位置,在此之后的元素在下一轮扫描中均不考虑
				newn = i;
			}
		n = newn;
	} while (newn > 0);
}

其实没什么用啦

三. 插入排序   O(n^2)   稳定

我的思路

给你一排数字,从第二数字开始 ,和之前的数字进行比较,按照大小插入到合适的位置。
比如 4,5,9,1,4,2,6,9,2,4
第一次排序后: 4,5,9,1,4,2,6,9,2,4
第二次排序后: 4,5,9,1,4,2,6,9,2,4
第三次排序后: 1,4,5,9,4,2,6,9,2,4
直到遍历完。

template <typename T>
void insertionSort(T arr[], int n) {
	for (int i = 1; i < n; i++) {
	//i从第二个元素开始

		int e = arr[i];  //存储要交换位置的元素
		int j;           //用j存储要交换的位置
		for (j = i; j > 0 && arr[j - 1] > e; j--) {//j最小为1
			arr[j] = arr[j - 1];
		}//此时的j位置正好是大于等于前一个数,小于后一个数的
		arr[j] = e;  //最后一步插入;
	}
}

提示:

1.插入排序在处理近乎有序数组的时候非常快,可以进化成O(n)的时间复杂度。

2.可以用来进行优化

蛮重要的

四. 希尔排序  O(n^1.3) 不稳定

我的思路

希尔排序是插入排序的升级版
有一组数:26,44,18,58,28,21,67,79,25,26,98,55
希尔排序是将它们分组后,对每组进行插入排序。

那分组怎么分呢?

可以选择一个初值,比如将组距设置为3,然后为2,最后为1
组距为3: 26,58,67,26
     44,28,79,98
     18,21,25,55
     58,67,26
     28,79,98
     21,25,55,
     67,26
     79,98
     25,55
分别对每组进行插入排序
组距为2:类似
最后组距为1,这是这一组数据已经近乎有序了
我们都知道插入排序对近乎有序的数组的时间复杂度为O(n);
这样性能就会有所提升。

template <typename T>
void shellSort(T arr[], int n) {
	int h = 1; //组距设置为1
	while (h < n / 3)   //根据某种推论,组距为3的倍数时,算法性能高一点
		h = h * 3 + 1;
	while (h >= 1) {//h最小为1
		for (int i = h; i < n; i++) { //插入排序
			T e = arr[i];
			int j;
			for (j = i; j >= h&&e < arr[j - h]; j -= h)
				arr[j] = arr[j - h];
			arr[j] = e;
		}
		h /= 3;    //每次除以3 知道h=1
	}
}

五. 归并排序  O(nlogn)    稳定

我的思路

把一排数一分为二,在把分好的二分之一,再一分为二,直到最小为1。
然后逐层往上,对每层进行归并排序。(瞎讲)
讲起来费力,不讲了,直接上代码。

template<typename T>
void insertionSort(T arr[], int l, int r) {
	for (int i = l + 1; i <=r; i++) {
		T e = arr[i];
		int j;
		for (j = i; j > l&&e < arr[j - 1]; j--)
			arr[j] = arr[j - 1];
		arr[j] = e;
	}
}
template <typename T>
void __merge(T arr[], int l, int mid, int r) {

	T* aux = new T[r - l + 1];//创建一个临时的空间
	for (int i = l; i <= r; i++) {
		aux[i - l] = arr[i];//复制arr
	}
	int i = l; int j = mid + 1;
	for (int k =l; k <= r; k++) {
		if (i > mid) {
			arr[k] = aux[j - l];
			j++;
		}
		else if (j > r) {
			arr[k] = aux[i - l];
			i++;
		}
		else if (aux[i - l] > aux[j - l]) {
			arr[k] = aux[j - l];
			j++;
		}
		else {
			arr[k] = aux[i - l];
			i++;
		}
	}

	delete[] aux;

}
template <typename T>
void __mergeSort(T arr[], int l, int r) {
	if (r - l <= 15) {//用插入排序对小数组进行优化
		insertionSort(arr, l, r);
		return;
	}
	int mid = (l + r) / 2;
	__mergeSort(arr, l, mid);//对左边归并
	__mergeSort(arr, mid + 1, r);//对右边归并
	if(arr[mid]>arr[mid+1])//如果左边的最大值小于右边的最小值,那么就省去这一步,在有序的数组中很有用。
	__merge(arr, l, mid, r);//进行排序
}
template <typename T>
void mergeSort(T arr[], int n)
{
	__mergeSort(arr, 0, n - 1);
}

还有一个自底向上的归并排序

这个不需要递归,只需要迭代

template<typename T >
//buttom up
void mergeSortBU(T arr[], int n) {
	for (int sz = 1; sz <= n; sz += sz) {sz就是每一组的大小,之后每次乘2
		for (int i =0; i+sz<n; i+=sz+sz) {
			__merge(arr, i, i + sz - 1, min(i + sz + sz - 1,n-1));//i+sz+sz-1可能大于n-1
		}
	}


}

六. 快速排序  O(nlogn)    不稳定

template<typename T>
int __partition(T arr[], int l, int r) {
swap(arr[l], arr[rand() % (r - l + 1) + l]);//从区间里随机找一个数
	T v = arr[l];//要比较的元素
	int j=l;//指向第一个位置 
	for (int i = l+1; i <= r; i++) {
		if (arr[i] < v) {
			swap(arr[++j], arr[i]);
		}
	}
	swap(arr[l], arr[j]);
	return j;
}
template<typename T>
void __quickSort(T arr[], int l, int r) {
	if (l >= r)return;
	int p = __partition(arr, l, r);
	__quickSort(arr, l, p-1);
	__quickSort(arr, p + 1, r);
}
template<typename T>
void quickSort(T arr[], int n) {
	__quickSort(arr, 0, n - 1);
}

对于有许多重复键值的用双路快排和三路快排

双路

template<typename T>
int partition2(T arr[], int l, int r) {
	swap(arr[l], arr[rand() % (r - l + 1) + l]);
	T v = arr[l];
	int i = l + 1;
	int j = r;
	//[l+1...i).......(j...r]
	while (true) {
		while (i <= r && arr[i] < v)i++;
		while (j >=l + 1 && arr[j] > v)j--;
		if (i > j)break;
		swap(arr[i], arr[j]);
		i++;
		j--;
	}
	swap(arr[l], arr[j]);
	return j;

}
template<typename T>
void __quickSort2(T arr[], int l, int r) {
	if (r - l <= 15) {
		insertionSort(arr, l, r);//做一个小优化
		return;
	}
	int p = partition2(arr, l, r);

	__quickSort2(arr, l, p - 1);
	__quickSort2(arr, p + 1, r);


}
template<typename T>
void quickSort2(T arr[], int n) {
	srand(time(NULL));
	__quickSort2(arr, 0, n - 1);
}

三路(在有重复键值的数中,三路比双路快)

template<typename T>
void __partition(T arr[], int l, int r) {
	if (l >= r)
		return;
	swap(arr[l], arr[rand() % (r - l + 1) + l]);
	T v = arr[l];
	int lt = l;
	int gt = r + 1;
	int i = l + 1;
	while (i < gt) {
		if (arr[i] < v) {
			swap(arr[++lt], arr[i]);
			i++;

		}
		else if (arr[i] > v) {
			swap(arr[--gt], arr[i]);
		
		}
		else {
			i++;
		}
	}
	swap(arr[l], arr[lt]);
	__partition(arr, l, lt - 1);
	__partition(arr,gt,r);
}
template<typename T>
void quickSort3(T arr[], int n) {
	srand(time(NULL));
	__partition(arr, 0, n - 1);

}

这是我自己实验下来的结果(100万个数排序)

1.在有重复键值的数组中,三路快排最快,(重要)
2.在随机数组中快排比归并快一点,(不重要)
3.在近乎有序的数组中,双路和归并快一点。(不重要)

七. 堆排序  O(nlogn)    稳定

template <typename T>
void shiftDown(T arr[], int n, int k) {
	while (2 * k + 1 < n) {
		int j = 2 * k + 1;
		if (j + 1 < n&&arr[j + 1] > arr[j])
			j+=1;
		if (arr[k] >= arr[j])break;
		swap(arr[k], arr[j]);
		k = j;
	}
}
template <typename T>
void heapSort(T arr[], int n) {
	for (int i = (n - 1) / 2; i >=0; i--)
		shiftDown(arr, n, i);
	for (int i = n - 1; i > 0; i--)
	{
		swap(arr[0], arr[i]);
		shiftDown(arr, i, 0);
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值