1. 冒泡排序
依次比较相邻两元素,若前一元素大于后一元素则交换之,直至最后一个元素即为最大;然后重新从首元素开始重复同样的操作,直至倒数第二个元素即为次大元素; 依次类推。如同水中的气泡,依次将最大或最小元素气泡浮出水面。
时间复杂度:O(N*N) 稳定性:稳定
#include "stdafx.h"
#include<iostream>
using namespace std;
void swap(int &a, int &b) {//交换函数int tmp = a;a = b;b = tmp;
}
void bubbleSort(int a[],int len) {for (int i = 0; i < len; ++i) {//排列len趟完成排序for (int j = 0; j < len - i - 1; ++j) {//每趟排序的元素都从第一个元素开始到尾部没有排序好的元素。第一趟为第一个元素到最后一个元素,排出了最大元素。第二趟为第一个元素到倒数第二个元素(排除以排序好的元素),排出第二大的元素。if (a[j] > a[j + 1]) {swap(a[j], a[j + 1]);}}}
}
int main() {int a[] = { 520,0,1,9,56,100,1,85,5,3,6 };int len = sizeof(a) / sizeof(a[0]);bubbleSort(a, len);for (int i = 0; i < len; ++i) {cout << " " << a[i];}getchar();return 0;
}
改进1:
void bubbleSort(int a[],int len) {int low = 0, high = len - 1;while (low < high) {//排序趟数。排序区间[low,high]。int flag = 0;//标志点for (int i = low; i < high; ++i) {//第一次正向冒泡if (a[i] > a[i + 1]) {swap(a[i], a[i + 1]);flag = i;}}high = flag;//表示标志点之后的元素已经排好,区间右值缩小。for (int i = high; i > low; --i) {//第2次反向冒泡if (a[i] < a[i - 1]) {swap(a[i], a[i - 1]);flag = i;}}low = flag;//表示断点之前的元素已经排好,区间左值缩小。 }
}
改进二:
void bubbleSort(vector<int> &arr,int bgn,int end){bool isLoop=true;/*isLoop用于指示依次遍历中是否发生元素交换,若没有,则已是有序数列,退出即可*/for(int i=end;i>=end&&true==isLoop;i--){isLoop=false;for(int j=1;j<i;j++){if(arr[j]<arr[j-1]){swap(arr[j],arr[j-1]);isLoop=true;}}}
}
2选择排序(Select Sort)
首先初始化最小元素索引值为首元素,依次遍历待排序数列,若遇到小于该最小索引位置处的元素则刷新最小索引为该较小元素的位置,直至遇到尾元素,结束一次遍历, 并将最小索引处元素与首元素交换;然后,初始化最小索引值为第二个待排序数列元素位置,同样的操作,可得到数列第二个元素即为次小元素;以此类推。
时间复杂度:O(N*N) 稳定性:不稳定
void selectSort(int a[], int len) {for (int i = 0; i < len - 1; ++i) {int minIndex = i;for (int j = i; j < len; ++j) {//找出无序区最小元素的下标if (a[j] < a[minIndex]) {minIndex = j;}}swap(a[i], a[minIndex]);//无序区第一个元素与最小值交换。}
}
3.插入排序(Insert Sort)
数列前面部分看为有序,依次将后面的无序数列元素插入到前面的有序数列中,初始状态有序数列仅有一个元素,即首元素。在将无序数列元素插入有序数列的过程中, 采用了逆序遍历有序数列,相较于顺序遍历会稍显繁琐,但当数列本身已近排序状态效率会更高。
时间复杂度:O(N*N) 稳定性:稳定
void insertSort(int a[], int len) {for (int i = 1; i < len; ++i) {int key = a[i];//保存无序区第一个元素为keyint j = i - 1;while (!(j <0) && a[j] > key) {//新元素在有序区寻找位置a[j + 1] = a[j];j--;}a[j+1] = key;}
}
4.希尔排序(Shell Sort)
插入排序的改进版。为了减少数据的移动次数,在初始序列较大时取较大的步长,通常取序列长度的一半,此时只有两个元素比较,交换一次;之后步长依次减半直至步长 为1,即为插入排序,由于此时序列已接近有序,故插入元素时数据移动的次数会相对较少,效率得到了提高。
时间复杂度:通常认为是O(N3/2) ,未验证 稳定性:不稳定
void shellSort(int a[], int len) {int gap = len ;while (gap = gap / 2) {//增量for (int i = gap; i < len; i++) {cout << i << " ";int key = a[i];//待排序元素int j = i - gap;for (; j+1>0&&a[j ] > key; j -= gap) {//插入排序a[j + gap] = a[j];}a[j + gap] = key;}}
}
5归并排序(Merge Sort)
采用了分治和递归的思想,递归&分治-排序整个数列如同排序两个有序数列,依次执行这个过程直至排序末端的两个元素,再依次向上层输送排序好的两个子列进行排序直 至整个数列有序(类比二叉树的思想,from down to up)。
时间复杂度:O(NlogN) 稳定性:稳定
void merge(int a1[], int na1, int a2[], int na2) {int tmp[1000];int t = 0, i = 0, j = 0,k=0;while( i < na1&&j < na2){if (a1[i] > a2[j]) {tmp[t++] = a2[j++];}else tmp[t++] = a1[i++];}while (i < na1) {tmp[t++] = a1[i++];}while (j < na2) {tmp[t++] = a2[j++];}while(k<t){a1[k] = tmp[k++];}
}
void mergeSort(int a[],int len) {if (len>1) {int mid = len / 2;int *a1 = a;int na1 = mid;int *a2 = a + mid ;int na2 = len - mid;mergeSort(a1, na1);mergeSort(a2, na2);merge(a1, na1, a2, na2);}
}
6.快速排序(Quick Sort)
(类似于选择排序的定位思想)选一基准元素,依次将剩余元素中小于该基准元素的值放置其左侧,大于等于该基准元素的值放置其右侧;然后,取基准元素的前半部分和 后半部分分别进行同样的处理;以此类推,直至各子序列剩余一个元素时,即排序完成(类比二叉树的思想,from up to down)
时间复杂度:O(NlogN) 稳定性:不稳定
void quickSort(int a[], int low, int high) {if (low < high) {int i = low - 1;int j = low;int key = a[high];//基准for (int j = low; j <= high; ++j) {//使比基准小或等于基准的元素前移。if (a[j] <=key) {++i;swap(a[i], a[j]);}}quickSort(a, low, i - 1);quickSort(a, i + 1, high);}
}
7.堆排序(Heap Sort)
堆排序的思想借助于二叉堆中的最大堆得以实现。首先,将待排序数列抽象为二叉树,并构造出最大堆;然后,依次将最大元素(即根节点元素)与待排序数列的最后一个 元素交换(即二叉树最深层最右边的叶子结点元素);每次遍历,刷新最后一个元素的位置(自减1),直至其与首元素相交,即完成排序。
时间复杂度:O(NlogN) 稳定性:不稳定
void heap(int a[],int node,int len) {//保证node结点是该节点及以下子节点的最大值int nodecopy = a[node];for (int i = 2 * node + 1; i < len; i = 2 * i + 1) {//遍历node所有下属节点if(i + 1 < len&&a[i + 1] > a[i]) i++;if (a[i] > a[node]) {a[node] = a[i];node = i;}}a[node] = nodecopy;//确定a[node]最终位置
}void heapSort(int a[],int len) {for (int node = len / 2 - 1; node >= 0; --node) {//将每个非叶结点进行堆调整,保证每个非叶节点是其子节点中最大值,循环完毕之后也就保证了a[0]是最大值heap(a, node, len);}for (int i = len-1;i>0; --i) {//将a[0](最大值)与i位置对换,然后再进行从0到i的堆调整swap(a[0], a[i]);heap(a, 0, i);}
}
8.桶排序
实现线性排序,但当元素间值得大小有较大差距时会带来内存空间的较大浪费。首先,找出待排序列中得最大元素max,申请内存大小为max + 1的桶(数组)并初始化为0; 然后,遍历排序数列,并依次将每个元素作为下标的桶元素值自增1;最后,遍历桶元素,并依次将值非0的元素下标值载入排序数列(桶元素>1表明有值大小相等的元素, 此时依次将他们载入排序数列),遍历完成,排序数列便为有序数列。
时间复杂度:O(x*N) 稳定性:稳定
/*桶排序*/
void bucketSort(vector<int> &arr)
{int max = getMaxValue(arr);int *pBuf = new int[max + 1];memset(pBuf, 0, (max + 1)*sizeof(int));for (auto const i : arr)++pBuf[i];for (int i = 0, j = 0; i <= max; ++i){while (pBuf[i]--)arr[j++] = i;}delete []pBuf;
}
9.基数排序(Radix Sort)
桶排序的改进版,桶的大小固定为10,减少了内存空间的开销。首先,找出待排序列中得最大元素max,并依次按max的低位到高位对所有元素排序;桶元素10个元素的大 小即为待排序数列元素对应数值为相等元素的个数,即每次遍历待排序数列,桶将其按对应数值位大小分为了10个层级,桶内元素值得和为待排序数列元素个数。
时间复杂度:O(x*N) 稳定性:稳定
int getnum(int a[], int len) {int max = a[0];int num = 1;for (int i = 1; i < len; ++i) {max=a[i] > max ? a[i] : max;}while (max /= 10) {num++;}return num;
}
void radixSort(int a[], int len) {int num = getnum(a, len);//获得位数vector<vector<int>>radix(10);for(int k=0;k<num;++k){for (int i = 0; i < len; ++i) {//存放元素int t = int(a[i] / pow(10, k))%10;radix[t].push_back(a[i]);}vector<vector<int> >::iterator p;vector<int>::iterator q;int i = 0;for (p = radix.begin(); p != radix.end(); ++p) {//取出元素for (q = (*p).begin(); q !=(*p).end(); ++q) {a[i++] = *q;}}for (int i = 0; i < 10; ++i) {//清空容器中元素if(!radix[i].empty())radix[i].clear();}}
}