1.冒泡排序
冒泡排序,如其名,该算法如同气泡上上升一样,
每次循环比较把最小的数交换到最前面,循环次数为n-1,时间复杂度为O(n^2)
void swap(vector<int>& arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//冒泡排序
void bubbleSort(vector<int>& arr){
if(arr.size() == 0)
return;
int n = arr.size();
for(int i = 0; i < n-1; i++){
for(int j = n-1; j > i; j--){
if(arr[j] < arr[j-1])
swap(arr, j-1, j);
}
}
//打印排序后数组
for(int i = 0; i < n; i++)
cout << arr[i] << ' ';
cout << endl;
}
int main(){
int myints[] = {9,3,6,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
bubbleSort(arr);
}
2.选择排序
思想:每次排序都把最小的放在前面,过程有点类似冒泡排序,但是选择排序是确定了最小的数后进行交换,冒泡排序是通过相邻元素的比较和交换,因此选择排序大大降低了交换的次数,时间复杂度为O(n^2),
void swap(vector<int>& arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
void selectSort(vector<int>& arr){
if(arr.size() == 0)
return;
int n = arr.size();
for(int i = 0; i < n-1; i++){
int min = i;
for(int j = n-1; j > i; j--){
if(arr[j] < arr[min])
min = j;
}
if(min != i)
swap(arr, min, i);
}
//打印排序后数组
for(int i = 0; i < n; i++)
cout << arr[i] << ' ';
cout << endl;
}
int main(){
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
selectSort(arr);
}
3.插入排序
思路:通过比较来找到合适的位置插入元素已达到排序目的。插入一个数时要保证前面的数已经是有序的,时间复杂度为O(n^2)
void insertSort(vector<int>& arr){
if(arr.size() == 0)
return;
int n = arr.size();
for(int i = 1; i < n; i++){//i从1开始,假设第一个数的位置正确
int target = arr[i];//arr[i]是待插入的数
int j = i;
for(; j > 0; j--){
if(arr[j-1] > target)
arr[j] = arr[j-1];
else
break;
}
arr[j] = target;
}
//打印排序后数组
for(int i = 0; i < n; i++)
cout << arr[i] << ' ';
cout << endl;
}
int main(){
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
insertSort(arr);
}
4.快速排序
思路:该排序算法的思想主要来源于冒泡排序,通过比较和交换大数小数来实现,在把小数放在前面的同时也把大数放到了后面,该算法还运用了二分法和递归分治的思想,但不稳定,其平均时间复杂度为O(nlogn)
void swap(vector<int>& arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
int partition(vector<int>& arr, int lo, int hi){
int pK = arr[lo];
int p = lo;
while(lo < hi){
//因为lo和hi相遇时,需要交换基准数和相遇的位置,一般选择第一个数为基准数,
//所以交换位置上的数应该比基准数小,所以hi先移动.
while(lo < hi && arr[hi] >= pK){
hi--;
}
while(lo < hi && arr[lo] <= pK){
lo++;
}
swap(arr, lo, hi);
}
swap(arr, lo, p);//交换基准值和相遇的位置
return lo;
}
void quickSort(vector<int>& arr, int lo, int hi){
if(lo >= hi)
return;
int pos = partition(arr, lo, hi);
quickSort(arr, lo, pos-1);
quickSort(arr, pos+1, hi);
}
int main(){
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
quickSort(arr, 0, arr.size()-1);
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
}
5.堆排序
所谓堆排序是利用堆来实现的选择排序,需要注意的是如果想升序排序就借助大顶堆,反之借助小顶堆,因为堆顶元素需要交换到序列的尾部。
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2] 或者
Key[i]>=Key[2i+1]&&key>=key[2i+2]
即任何一非叶节点的数值不大于或者不小于其左右孩子节点的数值。
因此满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的数值肯定是所有数值中最大的,小顶堆的堆顶的数值是所有数值中最小的。
主要操作过程如下:
1.初始化堆:将nums[1..n]构造为堆;
2.将当前无序区的堆顶元素nums[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
///<交换两个元素
void swap(vector<int>& nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
///<调整堆
void heapAdjust(vector<int>& nums, int start, int end){
int temp = nums[start];
for(int i = 2*start+1; i <= end; i = i*2+1){
///<选择左右子节点中数值较大的下标
if(i < end && nums[i] < nums[i+1])
i++;
if(temp >= nums[i])
break;///<已经为大顶堆
nums[start] = nums[i];///<将子节点上移
start = i;
}
nums[start] = temp;
}
///<堆排序
void heapSort(vector<int>& nums){
if(nums.size() == 0)
return;
///<建立大顶堆
for(int i = nums.size()/2; i >= 0; i--){
heapAdjust(nums, i, nums.size()-1);
}
///<将当前无序区的堆顶元素nums[1]同该区间的最后一个记录交换,
///<然后将新的无序区调整为新的堆
for(int i = nums.size()-1; i >= 0; i--){
swap(nums, 0, i);
heapAdjust(nums, 0, i-1);
}
}
int main()
{
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
heapSort(arr);
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
}
6.希尔排序
希尔排序也叫缩小增量排序,基本思想是先将整个待排记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时再对全体记录进行一次直接插入排序。
因此希尔排序的特点是,子序列的构成不是简单的逐段分割,而是将某个相隔某个增量的记录组成一个子序列。
代码如下:
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
///<
void shellInsert(vector<int>& nums, int k){
for(int i = k; i < nums.size(); i++){
int j = i - k;
int temp = nums[i];
///<从后往前,找比nums[i]小的数,若比其大,这往后移
while(j >= 0 && nums[j] > temp){
nums[j+k] = nums[j];
j -= k;
}
if(j != i-k)///<存在比其小的数
nums[j+k] = temp;
}
}
///<希尔排序
void shellSort(vector<int>& nums){
if(nums.size() == 0)
return;
int k = nums.size()/2;
while(k > 0){
shellInsert(nums, k);
k /= 2;
}
}
int main()
{
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
shellSort(arr);
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
}
7.归并排序
思想:利用递归,把问题划分成两个子问题,然后合并结果。
void merge(vector<int>& arr, int lo, int mid, int hi){
vector<int> v(hi-lo+1);
int i = lo;
int j = mid+1;
int k = 0;
while(i <= mid && j <= hi){
if(arr[i] < arr[j])
v[k++] = arr[i++];
else
v[k++] = arr[j++];
}
while(i <= mid)
v[k++] = arr[i++];
while(j <= hi)
v[k++] = arr[j++];
for(i = 0; i < v.size(); i++)
arr[lo+i] = v[i];
}
void mergeSort(vector<int>& arr, int lo, int hi){
if(lo >= hi)
return;
int mid = lo + (hi-lo)/2;
mergeSort(arr, lo, mid);//递归进行左边排序
mergeSort(arr, mid+1, hi);//递归进行右边排序
merge(arr, lo, mid, hi);
}
int main(){
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
mergeSort(arr, 0, arr.size()-1);
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
}
8.计数排序
该方法的时间复杂度为O(n),适用于待排序的数满足一定的范围时,不过计数排序需要比较多的辅助空间。其思想是,用待排序的数作为计数数组的下标,统计每个数字的个数。然后依次输出即可得到有序序列。
//<计数排序
void countSort(vector<int>& nums){
if(nums.size() == 0)
return;
int m = *max_element(nums.begin(), nums.end());///<找到数组中最大的数
vector<int> v(m+1, 0);///<v中保存着nums数组中每个元素的个数
for(int i = 0; i < nums.size(); i++){
v[nums[i]]++;
}
int idx = 0;
for(int i = 0; i <= m; i++){
for(int j = 0; j < v[i]; j++){
nums[idx++] = i;
}
}
}
int main()
{
int myints[] = {9,3,6,3,2,8};
vector<int> arr(myints, myints+sizeof(myints)/sizeof(int));
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
countSort(arr);
for(int i = 0; i < arr.size(); i++)
cout << arr[i] << ' ';
cout << endl;
}