排序
稳定排序:如果在排序的序列中,存在前后相同的两个元素,排序前和排序后他们的相对位置不发生变化;
内部排序:在排序过程中,所有元素调到内存中进行排序;
外部排序:待排序的元素太多,内存不能容纳全部的元素,在排序过程中需要对外存进行访问的排序过程;
冒泡排序
基本思想:首先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两个记录交换之,然后比较第二个记录和第三个记录的关键字。依次类推,直至第n-1个记录和第n个记录的关键字进行过比较为止。上述过程称做第一趟冒泡排序,其结果使得关键字最大的记录被安置到最后一个记录的位置上。然后进行第二趟冒泡排序,对前n-1个记录进行同样操作,其结果是使关键字次大的记录被安置到第n-1个记录的位置上。一般地,第i趟冒泡排序是从1到n-i+1依次比较相邻两个关键字,并在“逆序”时交换相邻记录,其结果是这n-i+1个记录中关键字最大的记录被交换到第n-i+1的位置上。
判别冒泡排序结束的条件应该是在一趟排序过程中没有进行过交换记录的操作。
#include<iostream>
using namespace std;
void BubbleSort(int a[],int len){
int i,temp,flag = 1;
while(flag){
flag = 0;
for(i = 1; i < len; i++)
if(a[i] < a[i - 1]){
temp = a[i];
a[i] = a[i-1];
a[i -1] = temp;
flag = 1;
}
}
}
int main(){
int a[]={1,6,2,5,4,7,0,11,12,5,8,4,56,24};
int len=sizeof(a)/sizeof(a[0]);
BubbleSort(a,len);
for(i=0;i<len;i++)
cout<<a[i]<<" ";
return 0;
}
选择排序
每次遍历都选择最大/最小值
void QuickSort(int * a,int len) {
for (int i = 0;i < len - 1; i++) {
int tmp = a[i];
int index = i;
for(int j = i + 1; j < len; j++) {
if (a[j] < tmp) {
tmp = a[j];
index = j;
}
}
a[index] = a[i];
a[i] = tmp;
}
}
插入排序
基本思想:将一个记录插入到已排好序的有序表中,从而得到一个新的、记录数增1的有序表。
时间复杂度为O(n^2),若待排记录序列为正序,时间复杂度可提高至O(n)。
直接插入排序
#include<iostream>
using namespace std;
int InsertSort(int *a, int length) {
if (NULL == a || length <= 0) {
return 0;
}
int i,j;
for (i = 1; i < length; i++) {
int tmp = a[i];
for (j = i; j > 0 && a[j - 1] > tmp; j--) {
a[j] = a[j - 1];
}
a[j] = tmp;
}
return 1;
}
int main(){
int a[]={9,9,8,7,5,1,3,2,4,6};
int length=sizeof(a)/sizeof(a[0]);
InsertSort(a,length);
for(int i = 0; i < length; i++) {
cout<<a[i]<< " ";
}
return 0;
}
折半插入排序
利用二分查找,减少了比较次数
int BinaryInsertSort(int *a, int length)
{
if (NULL == a || length <= 0) {
return 0;
}
int i,j,low,high,mid;
for (i = 1; i < length; i++)
{
int tmp = a[i];
low = 0;
high = i - 1;
//利用折半查找,减少查找次数
while (low <= high) {
mid = (low + high)/2;
if(tmp > a[mid])
low = mid + 1;
else
high = mid -1;
}
//high + 1就是i要插入的位置
for(j = i - 1; j >= high + 1; j--)
{
a[j + 1] = a[j];
}
a[high + 1] = tmp;
}
return 1;
}
希尔排序
希尔排序的实质就是分组插入排序,该方法又称缩小增量排序。
该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的。
void ShellSort(int a[], int n)
{
int j, step;
for (step = n / 2; step > 0; step /= 2)
for (j = step; j < n; j++) //从数组第step个元素开始
if (a[j] < a[j - step]) //每个元素与自己组内的数据进行直接插入排序
{
int temp = a[j];
int k = j - step;
while (k >= 0 && a[k] > temp)
{
a[k + step] = a[k];
k -= step;
}
a[k + step] = temp;
}
}
快速排序
基本思想:快速排序是对冒泡排序的一种改进。通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有效。
#include <iostream>
using namespace std;
//start开始索引,end结束索引
void QuickSort(int * a,int start, int end) {
if(start < end) {
int left = start;
int right = end;
int tmp = a[left];
while (left < right) {
while (left < right && a[right] >= tmp) {
right--;
}
a[left] = a[right];
while (left < right && a[left] <= tmp) {
left++;
}
a[right] = a[left];
}
a[left] = tmp;
QuickSort(a,start,left - 1);
QuickSort(a,left + 1,end);
}
}
int main(){
int a[]={9,8,7,56,4,3,1,2},i;
QuickSort(a,0,sizeof(a)/4 - 1);
for(i=0;i<sizeof(a)/4;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
堆排序
堆是具有下列性质的完全二叉树:
- 每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆;
- 每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆。
堆排序就是利用堆进行排序的方法。基本思想是:将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素的次大值。如此反复执行,便能得到一个有序序列了。
#include<iostream>
using namespace std;
void Heapfy(int* a,int index,int len) //建立最大堆
{
int left = index * 2 + 1;
int right = index * 2 + 2;
int large = index;
if(left < len && a[left] > a[large]) {
large = left;
}
if(right < len && a[right] > a[large]) {
large = right;
}
if(large != index) { //如果相等则说明无影响 不用交换
int temp = a[large]; //较大的节点值将交换到其所在节点的父节点
a[large] = a[index];
a[index] = temp;
Heapfy(a, large, len);
}
}
void HeapSort(int* a,int len)
{
for(int i = len/2 + 1; i >= 0; i--) {
Heapfy(a,i,len); //建立最大堆,将堆中最大的值交换到根节点
}
for(int i = len - 1; i >= 0; i--) {
int temp = a[0]; //将当前堆的根节点交换到堆尾的指定位置
a[0]=a[i];
a[i]=temp;
Heapfy(a,0,i); //建立下一次的最大堆
}
}
int main(){
int a[]={1,3,5,7,9,2,4,6,8,9};
HeapSort(a,10);
for(int i=0;i<10;i++){
cout<<a[i]<<" ";
}
return 0;
}
基数排序
基本思路:
- 判断数据在个位的大小,排列数据;
- 根据1的结果,判断数据在十分位的大小,排列数据。如果数据在这个位置的余数相同,那么数据之间的顺序根据上一轮的排列顺序确定;
- 依次类推,继续判断数据在百分位、千分位……上面的数据重新排序,直到所有的数据在某一位上数据都为0
该排序只限非负数,如果有负数可以用以下处理:
- 加上一个足够大的数N,使数组中的值都为正数,排序完成后再减去N;
- 正数,负数分开排序,负数也要转化为正数才可以排序;
#include <iostream>
#include <stdlib.h>
#include <queue>
using namespace std;
/*先对个位(修改存放顺序) 再对十位,百位 依次排序*/
queue<int> q[10];
int Sort(int *a,int len,int inter = 1)
{
if(a == NULL) {
return 0;
}
for(int i = 0; i < len; i++){
int t = (a[i]/inter) % 10;
q[t].push(a[i]);
}
int num = q[0].size();//统计桶子0中的元素个数,如果所有元素都放入了桶子0中,说明排序完成
int k = 0;
for(int i = 0; i<10; i++){
while(!q[i].empty()) {
a[k++] = q[i].front();
q[i].pop();
}
}
if(num == len) {
return 0;
}
Sort(a,len,inter*10);
return 1;
}
int main(){
int a[]={1,3,2,4,9,8,7,6,5,0};
Sort(a, sizeof(a)/4);
for(int i=0;i<sizeof(a)/4;i++)
cout<<a[i]<<" ";
return 0;
}
归并排序
将两个有序序列合并成一个有序序列。
#include<iostream>
using namespace std;
void mergearray(int a[], int first, int mid, int last, int temp[])
{
int i = first, j = mid + 1;
int m = mid, n = last;
int k = 0;
while (i <= m && j <= n)
{
if (a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while (i <= m)
temp[k++] = a[i++];
while (j <= n)
temp[k++] = a[j++];
for (i = 0; i < k; i++)
a[first + i] = temp[i];
}
void mergesort(int a[], int first, int last, int temp[])
{
if (first < last)
{
int mid = (first + last) / 2;
mergesort(a, first, mid, temp); //左边有序
mergesort(a, mid + 1, last, temp); //右边有序
mergearray(a, first, mid, last, temp); //再将二个有序数列合并
}
}
bool MergeSort(int a[], int n)
{
int *p = new int[n];
if (p == NULL)
return false;
mergesort(a, 0, n - 1, p);
delete[] p;
return true;
}
int main(){
int a[]={9,8,7,4,12,5,32,13};
MergeSort(a,sizeof(a)/sizeof(int));
for(int i=0; i < sizeof(a)/sizeof(int); i++)
cout<<a[i]<<" ";
return 0;
}
总结
排序方法 | 平均时间 | 最坏情况 | 辅助内存 | 稳定性 |
---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(1) | 不稳定 |
直接插入排序 | O(n^2) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
快速排序 | O(nlogn) | O(n^2) | O(logn) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
基数排序 | O(d(r+n)) | O(d(r+n)) | O(rd) | 稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(n) | 稳定 |
如果觉得本文对您有帮助,请点击‘顶’支持一下,您的支持是我写作最大的动力,谢谢。