这或许是东半球分析十大排序算法最好的一篇文章
十大经典排序算法动画与解析,看我就够了!
临时用的Dev,用到的函数必须先写在前面。
知乎这个写的不错
轻松搞定十大排序算法|C++版(上)
轻松搞定十大排序算法|C++版(下)
快排 QuickSort
思想:,先找到中间那个数pivot,,将大于pivot的放右边,将小于pivot的放左边,每次搞定一个数!(中间的一个数)。此时两边的数并不是有序的。
区别:归并排序是分两块,两块都排好序之后,再将两边排好序的数合再并成一个排序的序列。
1.选定pivot中心轴;
2.将大于pivot的数字放在pivot的右边;
3.将小于pivot的数字放在pivot的左边;
4.分别对左右子序列重复前三步操作。
#include<iostream>
using namespace std;
int partition(int* arr,int left,int right){
int pivot=arr[left]; //注意
while(left<right){ //注意
while(arr[right]>=pivot && left<right) right--;
arr[left]=arr[right];//这覆盖可以,写swap函数交换也可以,道理是一样的
while(arr[left]<=pivot && left<right) left++;
arr[right]=arr[left];
}
arr[left]=pivot;
return left;
}
//快速排序
void quick_sort(int* arr, int left,int right){
if(left<right){ //注意
int index=partition(arr,left,right);
quick_sort(arr,left,index-1);
quick_sort(arr,index+1,right);
}
}
int main(){
int arr[]={1,1,5,8,3,7,4,1,1};
int size=sizeof(arr)/sizeof(arr[0]); //计算数组大小
quick_sort(arr,0,size-1); //确切下标
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
/*交换函数
void swap(int* arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
*/
/* 左神的partition函数用从C++改的能跑通
int partition(int* arr, int l, int r) {
int less = l - 1;
int more = r;
while (l < more) {
if (arr[l] < arr[r]) {
swap(arr, ++less, l++);
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
} else {
l++;
}
}
swap(arr, more, r);
int w[2]={less + 1, more};
return w;
}
*/
归并排序 MergeSort
我们将本是无序的数组序列,通过两两合并排序之后再合并,最终获得一个有序的数组。
需要辅助数组
左边排好,右边排好,再两边合并排好。
#include<iostream>
using namespace std;
void merge(int* arr, int l, int m, int r) {//这个函数比快排多个参数,m
int* help = new int[r - l + 1]; //注意新建数组只能只能这样建 int*
int i = 0;
int p1 = l;//指向左侧部分第一个数
int p2 = m + 1;//指向右侧部分第一个数
while (p1 <= m && p2 <= r) { //谁小填谁,相当于个排序
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) { //把剩下的追加到后面,如果p1没越界,p2必越界。
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < (r-l+1); i++) {
arr[l + i] = help[i]; //注意l+i //把help里的数复制到原数组arr
}
}
//归并排序
void mergeSort(int* arr, int l, int r) {
if (l == r) {//注意这个条件 //只有一个数时
return;
}
int mid = l + ((r - l) >> 1); // (l+r)/2
mergeSort(arr, l, mid); //左部分排好
mergeSort(arr, mid + 1, r);//右部分排好
merge(arr, l, mid, r); //再合并排序
}
int main(){
int arr[]={9,1,5,8,3,7,4,1,1,2};
int size=sizeof(arr)/sizeof(arr[0]);
mergeSort(arr, 0, size-1);
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
原来的左神的,多了一段。暂时不用。
#include<iostream>
using namespace std;
void swap(int* arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//归并排序
void merge(int* arr, int l, int m, int r) {
int* help = new int[r - l + 1]; //辅助数组
int i = 0;
int p1 = l;//指向左侧部分第一个数
int p2 = m + 1;//指向右侧部分第一个数
while (p1 <= m && p2 <= r) { //谁小填谁
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) { //如果p1没越界,p2必越界
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < (r-l+1); i++) {
arr[l + i] = help[i]; //把help里的数复制到原数组arr
}
}
void mergeSort(int* arr, int l, int r) {
if (l == r) {//只有一个数
return;
}
int mid = l + ((r - l) >> 1); // (l+r)/2
mergeSort(arr, l, mid); //左部分排好
mergeSort(arr, mid + 1, r);//右部分排好
merge(arr, l, mid, r);
}
//归并排序
void mergeSort(int* arr, int length) {
if (arr == NULL || length < 2) {
return;
}
mergeSort(arr,0,length - 1);
}
int main(){
int arr[]={9,1,5,8,3,7,4,1,1,2};
int size=sizeof(arr)/sizeof(arr[0]);
mergeSort(arr,size);
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
堆排序 HeapSort
堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
注意红框内容:
思路:
那个二叉树只是为计算而想象成的一颗二叉树。
1.让一个数组变成大根堆(并不是有序),第一步就把大顶推建立好,
建立好的大顶推像上图一样。
2.建立好大根堆之后,堆顶位置为整个数组最大值,让它和最后位置交换,,
换完之后,最大值来到了数组最后位置;
3.此时,最后位置为最大值,不动了,让堆大小减一;剩下的做heapify处理
4.每次搞定一个末尾的值
#include<iostream>
using namespace std;
void swap(int* arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//向上走。
//第一步先整体建立起一个大顶推,后面才好说。。建立大根堆过程(无序的)
void heapInsert(int* arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {//如果比当前父节点位置大,就和父位置交换
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;// index往上跑,一直往上跑
}
}
//向下走。
//其中有一个值变小,往下沉的过程,重新变成大根堆
//size为最右的边界,size是取不到的
void heapify(int* arr, int index, int size) {
int left = index * 2 + 1;
//下面2句的意思是找到三者之间最大的(根/左/右)
while (left < size) { //注意while只判断left
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//选出左右孩子中较大的那个
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
//堆排序
void heapSort(int* arr, int size) {
if (arr == NULL || size< 2) {
return;
}
for (int i = 0; i < size; i++) {//建立大顶推的过程
heapInsert(arr, i);//循环发生完之后,整个数组就变成大根堆了
}
swap(arr, 0, size-1);//建立好大根堆之后,堆顶位置为整个数组最大值,让它和最后位置交换,,换完之后,最大值来到了数组最后位置
size--;
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, size-1);//循环操作上述过程
size--;
}
}
int main(){
int arr[]={9,1,5,8,3,7,4,1,1,2};
int size=sizeof(arr)/sizeof(arr[0]);
heapSort(arr,size);
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
下面这个暂时不用。。之前 heapSort 函数的最后一点是这么写的,不好理解,所以换成了上面那种。其实是一样的,只不过浓缩了。
//堆排序
void heapSort(int* arr, int length) {
if (arr == NULL || length < 2) {
return;
}
for (int i = 0; i < length; i++) {
heapInsert(arr, i);//循环发生完之后,整个数组就变成大根堆了
}
int size = length;
//我说的就是下面这点。。。
swap(arr, 0, --size);//建立好大根堆之后,堆顶位置为整个数组最大值,让它和最后位置交换,,换完之后,最大值来到了数组最后位置
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);//循环操作上述过程
}
}
冒泡 BubbleSort
#include<iostream>
using namespace std;
void swap(int* arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//冒泡排序
void bubbleSort(int* arr, int size){
if (arr == NULL || size < 2) {
return;
}
for (int e = size - 1; e > 0; e--) {//e先指向最后一个数
for (int i = 0; i < e; i++) { //从0至倒数第二个数
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
}
}
}
}
int main(){
int arr[]={9,1,5,8,3,7,4,1,1,2};
int size=sizeof(arr)/sizeof(arr[0]);
bubbleSort(arr,size);
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
冒泡优化版
如果发现在某一趟排序的过程中,数组已经排序了,此时不用再继续排序。
避免了因有序情况下的无意义循环判断。
比如在第三趟,发现已经有序了,并没有执行交换操作,所以flag为true,跳出循环。
//冒泡排序优化
void bubbleSort(int* arr, int size){
if (arr == NULL || size < 2) {
return;
}
for (int e = size - 1; e > 0; e--) { //有这么多趟
bool flag = true; //设置标记
for (int i = 0; i < e; i++) {//开始交换
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
flag =false;//如果有交换 ,就设为false,一旦没有交换,还是true不变
}
}
if(flag) break;
}
}
选择排序 SelectionSort
#include<iostream>
using namespace std;
void swap(int* arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//选择排序
void selectionSort(int* arr, int length) {
if (arr == NULL || length < 2) {
return;
}
for (int i = 0; i < length - 1; i++) { //0~倒数第二个
int minIndex = i;
for (int j = i + 1; j < length; j++) { //从i+1直到最后去选择一个最小的,然后交换
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
int main(){
int arr[]={9,1,5,8,3,7,4,1,1,2};
int size=sizeof(arr)/sizeof(arr[0]);
selectionSort(arr,size);
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
插入排序 InsertionSort
将一个记录插入到已经排好序的有序序列中,从而得到一个新的、记录数增1的有序表。
#include<iostream>
using namespace std;
void swap(int* arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//插入排序
void insertionSort(int* arr, int length) {
if (arr == NULL || length < 2) {
return;
}
for (int i = 1; i < length; i++) {//0位置不用管,从第1位置到最后一个位置都要去比较
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
int main(){
int arr[]={9,1,5,8,3,7,4,1,1,2};
int size=sizeof(arr)/sizeof(arr[0]);
insertionSort(arr,size);
for(int i=0;i<size;i++){
//printf("%d",arr[i]);
cout<<arr[i];
}
return 0;
}
左神(JAVA)
冒泡排序
package basic_class_01;
import java.util.Arrays;
public class Code_00_BubbleSort {
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int e = arr.length - 1; e > 0; e--) {
for (int i = 0; i < e; i++) {
if (arr[i] > arr[i + 1]) {
swap(arr, i, i + 1);
}
}
}
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
// for test
public static void comparator(int[] arr) {
Arrays.sort(arr);
}
// for test
public static int[] generateRandomArray(int maxSize, int maxValue) {
int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
}
return arr;
}
// for test
public static int[] copyArray(int[] arr) {
if (arr == null) {
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
// for test
public static boolean isEqual(int[] arr1, int[] arr2) {
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
return false;
}
if (arr1 == null && arr2 == null) {
return true;
}
if (arr1.length != arr2.length) {
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
// for test
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// for test
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
bubbleSort(arr1);
comparator(arr2);
if (!isEqual(arr1, arr2)) {
succeed = false;
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
int[] arr = generateRandomArray(maxSize, maxValue);
printArray(arr);
bubbleSort(arr);
printArray(arr);
}
}
简单选择排序
package basic_class_01;
import java.util.Arrays;
public class Code_02_SelectionSort {
public static void selectionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
直接插入排序
package basic_class_01;
import java.util.Arrays;
public class Code_01_InsertionSort {
public static void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {//0位置不用管,从第1位置到最后一个位置都要去比较
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
}
堆排序
package basic_class_01;
import java.util.Arrays;
public class Code_03_HeapSort {
1.让一个数组变成大根堆(并不是有序)
2.建立好大根堆之后,堆顶位置为整个数组最大值,让它和最后位置交换,,换完之后,最大值来到了数组最后位置;
3.此时,最后位置为最大值,不动了,让堆大小减一;剩下的做heapify处理
4.每次搞定一个末尾的值
public static void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);//循环发生完之后,整个数组就变成大根堆了
}
int size = arr.length;
swap(arr, 0, --size);//建立好大根堆之后,堆顶位置为整个数组最大值,让它和最后位置交换,,换完之后,最大值来到了数组最后位置
while (size > 0) {
heapify(arr, 0, size);
swap(arr, 0, --size);//循环操作上述过程
}
}
//建立大根堆过程(无序的)
public static void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {//如果比当前父节点位置大,就和父位置交换
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;// index往上跑
}
}
//其中有一个值变小,往下沉的过程,重新变成大根堆
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while (left < size) {
int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//选出左右孩子中较大的那个
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
归并排序
package basic_class_01;
import java.util.Arrays;
public class Code_05_MergeSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
mergeSort(arr, 0, arr.length - 1);
}
public static void mergeSort(int[] arr, int l, int r) {
if (l == r) {//只有一个数
return;
}
int mid = l + ((r - l) >> 1); // (l+r)/2
mergeSort(arr, l, mid); //左部分排好
mergeSort(arr, mid + 1, r);//右部分排好
merge(arr, l, mid, r);
}
public static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1]; //辅助数组
int i = 0;
int p1 = l;//指向左侧部分第一个数
int p2 = m + 1;//指向右侧部分第一个数
while (p1 <= m && p2 <= r) { //谁小填谁
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) { //如果p1没越界,p2必越界
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i]; //把help里的数复制到原数组arr
}
}
}
快速排序
package basic_class_01;
import java.util.Arrays;
public class Code_04_QuickSort {
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int l, int r) {
if (l < r) {
//swap(arr, l + (int) (Math.random() * (r - l + 1)), r); //随机快排
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0] - 1);
quickSort(arr, p[1] + 1, r);
}
}
public static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
while (l < more) {
if (arr[l] < arr[r]) {
swap(arr, ++less, l++);
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
} else {
l++;
}
}
swap(arr, more, r);
return new int[] { less + 1, more };
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
计数排序
适用于量大、范围小。
时间复杂度:O(n+k);
空间复杂度:O(n+k);
算法思路:
(1)开辟一个数组用于记录每个数字出现的次数;
(2)再开辟一个数组用于存放最后的结果(其实可以直接修改原数组,不用开辟新的空间);
计数完成:
再把值依次放到一个新数组:
C++代码实现:
用指针出现了错误,所以我用了vector开辟空间。
#include<iostream>
#include<vector>
using namespace std;
void CountSort(vector<int>& arr, int length) {
//通过max计算出count数组所需要开辟的空间大小
int max = arr[0], min = arr[0];
for (int i = 0; i < length; i++) {
if (arr[i] > max)
max = arr[i];
if (arr[i] < min)
min = arr[i];
}
//vector<int> res(length);//开辟新的数组
int range = max - min + 1;
vector<int> count(range);
for (int i = 0; i < length; i++) {
//注意:这里在存储上要在原始数组数值上减去min才不会出现越界问题
count[arr[i] - min]++;
}
int j = 0;
for (int i = 0; i < range; i++) {
while (count[i]--) {
arr[j++] = i + min;//注意:要将i的值加上min才能还原到原始数据
}
}
// return res;
}
int main() {
vector<int> arr { 3, 4, 3, 2, 1, 2, 6, 5, 4, 7 };
CountSort(arr, arr.size());
for (int i = 0; i < arr.size(); i++) {
cout << arr[i]<<" ";
}
system("pause");
return 0;
}
桶排序
桶排序的原理是将数组分到有限数量的桶中,再对每个桶子再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后将各个桶中的数据有序的合并起来。
排序过程:
假设待排序的一组数统一的分布在一个范围中,并将这一范围划分成几个子范围,也就是桶
将待排序的一组数,分档规入这些子桶,并将桶中的数据进行排序
将各个桶中的数据有序的合并起来
void bucketSort(float arr[], int n)
{
// 1) Create n empty buckets
vector<float> b[n];
// 2) Put array elements in different buckets
for (int i=0; i<n; i++)
{
int bi = n*arr[i]; // Index in bucket
b[bi].push_back(arr[i]);
}
// 3) Sort individual buckets
for (int i=0; i<n; i++)
sort(b[i].begin(), b[i].end());
// 4) Concatenate all buckets into arr[]
int index = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < b[i].size(); j++)
arr[index++] = b[i][j];
}
另外的讲解
桶排序的基本思想是假设数据在[min,max]之间均匀分布,其中min、max分别指数据中的最小值和最大值。那么将区间[min,max]等分成n份,这n个区间便称为n个桶。将数据加入对应的桶中,然后每个桶内单独排序。由于桶之间有大小关系,因此可以从大到小(或从小到大)将桶中元素放入到数组中。
** 特别的,当将区间[min,max]限定在[0,1)时**
/*算法:桶排序*/
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void bksort(float A[],int l,int h){
int size = h-l+1;
vector<float> b[size];//有size个数据,就分配size个桶
for(int i=l;i<=h;i++){
int bi = size*A[i];//元素A[i]的桶编号
b[bi].push_back(A[i]);//将元素A[i]压入桶中
}
for(int i=0;i<size;i++)
sort(b[i].begin(),b[i].end());//桶内排序
int idx = l;//指向数组A的下标
for(int i=0;i<size;i++){//遍历桶
for(int j=0;j<b[i].size();j++){//遍历桶内元素
A[idx++] = b[i][j];
}
}
}
int main(){
float A[] = {0.78,0.17,0.39,0.26,0.72,0.94,0.21,0.12,0.23,0.68};
bksort(A,2,9);
for(int i=0;i<10;i++)
cout<<A[i]<<" ";
}