今天我们来讲排序
十大排序算法
1. 冒泡排序
每一轮都从头开始比较, 比较当前数与后一位数,若当前数大于后一位数则进行交换,每一轮都会在末尾得到一位排序正确的数,因此每一轮比较结束,下一轮的比较范围将缩小(j<n-1-i)
void BubbleSort(int *a,int n)
{
if (n <=1)
return;
for (int i = 0; i<n; i++)
{
bool isChange = false; //判断某一次排序是否有元素交换,若无元素交换,则数组已经有序,可以提前退出
for (int j = 0; j < n - 1 - i; j++)
{
if (a[j] > a[j + 1])
{
int temp = a[j + 1];
a[j + 1] = a[j];
a[j] = temp;
isChange = true;
}
}
if (!isChange)
break;
}
}
2. 插入排序
每一轮将当前数(a[i])插入到前面已经排序好的合适位置,因此关键是如何找到该合适位置通过不断比较以及移动来找到合适的位置
void InsertionSort(int *a, int n)
{
for (int i = 1; i < n; i++)
{
int value = a[i];
int j = i - 1;
for (j; j >= 0; j--)
{
if (a[j] > value)
a[j + 1] = a[j];
else
{
break;
}
}
a[j + 1] = value;
}
}
3. 希尔排序
插入排序的改进
void shellSort(int* a,int n)
{
int d = n / 2;
while (d>=1)
{
for (int i = d; i <n; i ++)
{
int value = a[i];
int j = i - d;
for (j; j >= 0; j -= d)
{
if (a[j] > value)
a[j + d] = a[j];
else
break;
}
a[j + d] = value;
}
d = d / 2;
}
}
4. 选择排序
每一轮从未排序的数组中,选择最小的数放在排序数组的后面,有n个数,需要进行n-1次选择,第一次选出最小的数,第二次选择第2小的数,第三次选出第3小的数。。。。
void SelectionSort(int *a,int n)
{
for (int i = 0; i < n - 1; i++)
{
int min = i;
for (int j = i+1; j < n; j++)
{
if (a[j] < a[min])
min = j;
}
if (i != min)
{
int temp = a[i];
a[i] = a[min];
a[min] =temp;
}
}
}
5. 归并排序
分治思想,从下而上进行排序
void merge(int *a, int l, int mid, int r)
{
int *L = new int[mid - l + 1]; //左边数组
int *R = new int[r - mid]; //右边数组
for (int i = l; i <= mid; i++)
{
L[i - l] = a[i]; //得到左半数组
}
for (int j = mid + 1; j <= r; j++)
{
R[j - mid - 1] = a[j]; //得到右半部分数组
}
int m = 0, n = 0, t = l;
while (m < (mid - l + 1) && n < (r - mid)) //循环比较左边和右边数组,选出最小的数并重新放回原数组(t++)
{
while (m < (mid - l + 1) && L[m] <= R[n])
a[t++] = L[m++];
while (n < (r - mid) && R[n] < L[m])
a[t++] = R[n++];
}
while(m < (mid - l + 1)) //判断左边数组是否有剩余,若有则直接放在原数组a的后面
a[t++] = L[m++];
while(n < (r - mid))
a[t++] = R[n++];
delete[] L, R; //释放内存
}
void mergeSort(int *a, int l, int r)
{
if (l >= r) return; //递归结束条件
int mid = l + (r - l) / 2;
mergeSort(a, l, mid); //对左边递归
mergeSort(a, mid + 1, r); //对右边递归
merge(a, l, mid, r); //合并左,右数组 (从下而上排序)
}
6. 快速排序
分治思想,从上而下排序
int findPivot(int *a, int start, int end)
{
int pivot = a[start]; //令第一个数为分区点,将小于分区点数放在左边,大于分区点的数放在右边
while (start < end)
{
while (start < end&&a[end] >= pivot) //从末尾开始,若大于分区点,则左移一位继续判断,
end--;
a[start] = a[end]; //直到找到第一个小于分区点的数,并与start交换放在左边
while (start < end&&a[start] <= pivot) //同理,再从左边start开始,若小于分区点,则右移继续判断
start++;
a[end] = a[start]; //直到找到大于分区点的数,并且与end交换放在右边,
}
a[start] = pivot; //此时start=end,
return start;
}
void quickSort(int *a, int start, int end)
{
if (start >= end) return;
int pivot = findPivot(a, start, end); //(从上而下排序)
quickSort(a, start, pivot);
quickSort(a, pivot + 1, end);
}
7. 堆排序
堆排序的思路为:先建立一个大顶堆,然后循环交换堆顶元素和最后一个元素,并且重新调整大顶堆
void makeHeap(int *a, int i, int n) //大顶堆建立方法(从上而下调整堆结构)
{
while (i < n)
{
int max = i; //根节点,并假设根节点为最大值
int lchild = 2 * i + 1; //左孩子
int rchild = 2 * i + 2; //右孩子
if (lchild<n&&a[lchild]>a[max]) //寻找根节点,左,右孩子中的最大值,将最大值放在根节点处
max = lchild;
if (rchild<n&&a[rchild]>a[max])
max = rchild;
if (max != i) //若最大值为孩子节点,则交换孩子节点和最大值
{
int temp = a[i];
a[i] = a[max];
a[max] = temp;
i = max; //然后将刚刚交换的孩子节点设为根节点,继续调整下一层子树的堆,(,因为交换根节点和孩子节点后,可能导致子树的堆结构遭到破坏。需要从上往下不断调整)
}
else break; //若最大值还是原来的根节点,则不需要调整,直接退出即可
}
}
void heakSort(int *a,int n) //堆排序的实现
{
for (int i = n / 2 - 1; i >= 0;i-- ) //从最后一个非叶子节点开始,构建一个大顶堆
{
makeHeap(a, i, n);
}
for (int i = n - 1; i >= 0; i--) //将堆顶的最大值,放在数组末尾,然后重新调正大顶堆,此时只需要从根节点往下调整一次即可
{
int temp = a[i];
a[i] = a[0];
a[0] = temp;
makeHeap(a, 0, i);
}
}
8. 计数排序
以空间换时间
void countSort(int *a, int n)
{
int max = a[0];
for (int i = 0; i < n; i++) {
if (a[i] > max)
max = a[i]; //找到原数组中的最大值
}
int *temp = new int[max+1]; //建立临时数组,数组下标表示原数组的元素,如:temp[20]=2,表示a中20 出现两次
for (int i = 0; i < max + 1; i++)
temp[i] = 0;
for (int i = 0; i < n; i++)
temp[a[i]]++;
int index = 0;
for (int i = 0; i < max + 1; i++)
{
for (int j = 0; j < temp[i]; j++)
a[index++] = i;
}
delete[] temp; //释放内存
}
9. 桶排序
同样是以空间换取时间
void bucketSort(int* a,int n){
int min = a[0];
int max = a[0];
for (int i = 0; i < n; i++) //找到原数组中的最大、最小值
if (min > a[i])
min = a[i];
else
if (max < a[i])
max = a[i];
int b = (max - min) / 5 + 1; //每个桶的容量
int** temp; //二维数组
temp = new int*[5];
for (int i = 0; i < 5; i++)
{
temp[i] = new int[n+1]; //建立5个桶,容量为n+1,
}
for (int i = 0; i < 5; i++)
temp[i][n] = 0; //每个桶的最后一位表示桶中的元素个数
for (int i = 0; i < n; i++)
{
int index = (a[i] - min) /b; //将原数组中的元素映射到相应的桶中
temp[index][temp[index][n]++] = a[i]; //元素加入桶中的同时,相应的桶的最后一位加一,表示桶中元素个数加一
}
int i = 0;
for (int j = 0; j < 5; j++)
{
int num = temp[j][n]; //每个桶的数量
if (num!= 0)
{
int *sort = new int[num];
for (int f = 0; f <num; f++)
sort[f] = temp[j][f];
InsertionSort(sort, num); //对每个桶内部进行排序
for (int s = 0; s <num; s++)
a[i++] = sort[s]; //以此将排序后桶中的元素放回原数组
delete[] sort; //内存释放
}
}
for (int i = 0; i < 5; i++) //释放二维数组的内存
{
delete[] temp[i];
}
delete[] temp;
}
10. 基数排序
思路:将数组中的元素先按个为进行排序,然后再按十位进行排序,再按百位进行排序,,,,,以此类推
void radixSort(int* a ,int n)
{
int max = a[0];
for (int i = 0; i < n; i++) //找到原数组中的最大值
{
if (a[i] > max)
max = a[i];
}
int maxnum = 0;
int max1 = max;
while (max1!=0) //确定原数组中最大值的位数,桶排序需要进行maxnun次排序
{
max1 = max1 / 10;
maxnum++;
}
int** temp = new int*[10]; //建立10个桶
for (int i = 0; i < 10; i++)
{
temp[i] = new int[n+1]; //每个桶容量为n+1,最后一个位表示桶中元素个数
temp[i][n] = 0;
}
int d = 1;
while (maxnum>0)
{
for (int i = 0; i < n; i++)
{
int index = (a[i]/d) % 10; //按位进行储存
temp[index][temp[index][n]++] = a[i]; //将元素放入相应的桶中,如个位是3的元素,放入下标为3的桶中
}
int j = 0;
int q = 0;
while (q<10)
{
if (temp[q][n] != 0)
{
for (int i = 0; i < temp[q][n]; i++)
{
a[j++] = temp[q][i]; //按位的大小进行排序
temp[q][i] = 0; //清除桶中的数据,以便桶可以进行下次储存数据
}
temp[q][n] = 0;//将桶中用于表示桶内数据个数的值置为0,以便下次使用桶
}
q++;
}
maxnum--;
d = d * 10;
}
for (int i = 0; i < 10; i++)
{
delete[] temp[i];
}
delete[] temp;
}