1.冒泡排序
算法原理:比较相邻的元素,如果前一个比后一个大,就把它们两个调换位置。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
注意:第二个for循环j < length - i - 1要注意再-1。
void bubbleSort(int *arr,int n)
{
int m,i,j;
for(i = 0;i < n;i++)
{
for(j = 0;j < n-1-i;j++)
{
if(arr[j] > arr[j+1])
{
m = arr[j];
arr[j] = arr[j+1];
arr[j+1] = m;
}
}
}
}
2.选择排序
算法原理:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。
void selectionSort(int *arr,int n)
{
for(int i = 0;i < n;i++)
{
int index = i;
for(int j = i+1;j < n;j++)
{
if(arr[j] < arr[index])
{
index = j;
}
}
if(index == i)
{
continue;
}
else
{
int temp;
temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
}
3.插入排序
算法原理:
1)从第一个元素开始,该元素可以认为已经被排序
2)取出下一个元素,在已经排序的元素序列中从后向前扫描
3)如果该元素(已排序)大于新元素,将该元素移到下一位置
4)重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
5)将新元素插入到该位置后
6)重复步骤2~5
void insertSort(int *arr,int n)
{
int i,j,target;
for(i = 1;i < n;i++)
{
j = i;
target = arr[i];
while(j > 0 && target < arr[j - 1])
{
arr[j] = arr[j - 1];
j--;
}
arr[j] = target;
}
}
4.归并排序
算法步骤:
- 分:将一个大数组分为两个小数组, 一直往小了分直到划分后的数组长度为1时停止,此时将每一个子数组视为有序子序列。
- 治:将两个有序的子数组合并为更大的有序的数组直至合并成的数组为整个序列。 工作原理:
第一步:申请内存空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 重复步骤3直到某一指针超出序列尾
第四步:将另一序列剩下的所有元素直接复制到合并序列尾
void Merge(int *arr, int start, int mid, int end, int * temp)
{
//合并两个有序序列
int length = 0;
//表示辅助空间有多少个元素
int i_start = start;
int i_end = mid;
int j_start = mid + 1;
int j_end = end;
while (i_start <= i_end && j_start <= j_end)
{
if (arr[i_start] < arr[j_start])
{
temp[length++] = arr[i_start];
i_start++;
}
else
{
temp[length++] = arr[j_start];
j_start++;
}
}
while (i_start <= i_end)
{
temp[length++] = arr[i_start];
i_start++;
}
while (j_start <= j_end)
{
temp[length++] = arr[j_start];
j_start++;
}
//把辅助空间的数据放到原空间
for (int i = 0; i < length; i++)
{
arr[start + i] = temp[i];
}
}
void MergeSort(int *arr, int start, int end, int * temp)
{
if (start >= end)
return;
int mid = (start + end) / 2;
MergeSort(arr, start, mid, temp);
MergeSort(arr, mid + 1, end, temp);
Merge(arr, start, mid, end, temp);
}
int main()
{
int a[8] = { 6, 5, 3, 1, 8, 7, 2, 4 };
int b[8] = { 0 };
MergeSort(a, 0, 8 - 1, b); //要-1
for(int i = 0; i < 8; ++i)
{
printf("%d ",b[i]);
}
return 0;
}
5.快速排序
1)从序列中挑出一个元素,作为”基准”(pivot).
2)把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
3)对每个分区递归地进行步骤1~2,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。
/*************非随机化快速排序代码如下:*******************/
int Partition(int* arr, int length, int start, int end)
{
if(NULL == arr || length <= 0 || start < 0 || end <= 0 || end >= length)
return -1;
int baseVal = arr[start];
int m_start = start;
int m_end = end;
while(m_start < m_end) //非随机快排和随机快排的循环是不一样的。
{
while(m_start < m_end && arr[m_end] >= baseVal) //从右向左找比基准数小的数 //至少要有个是有“等于”的
--m_end;
if(m_start < m_end)
{
arr[m_start] = arr[m_end];
++m_start;
}
while(m_start < m_end && arr[m_start] < baseVal) //从左向右找比基准数大的数
++m_start;
if(m_start < m_end)
{
arr[m_end] = arr[m_start];
--m_end;
}
}
arr[m_start] = baseVal;//把基准数放到i的位置
return m_start;
}
void QuickSort(int* arr, int length, int start, int end) //非随机快排和随机快排只有partition函数不同,此时还是相同的
{
if(start == end)
return;
int index = Partition(arr, length, start, end);
if(index > start)
QuickSort(arr, length, start, index - 1);
if(index < end)
QuickSort(arr, length , index + 1, end);
}
int main()
{
int arr[] = {
5,8,8,65,3,4,71,2,5,120, 546
};
QuickSort(arr, sizeof(arr)/sizeof(int), 0, sizeof(arr)/sizeof(int) - 1);
for(int i = 0; i < sizeof(arr)/sizeof(int); ++i)
{
printf("%d ",arr[i]);
}
return 0;
}
6.堆排序
堆排序的基本过程:
- 将n个元素的序列构建一个大顶堆或小顶堆
- 将堆顶的元素放到序列末尾
- 将前n-1个元素重新构建大顶堆或小顶堆,重复这个过程,直到所有元素都已经排序
整体时间复杂度为nlogn
#include<iostream>
#include<vector>
using namespace std;
void swap(vector<int>& arr, int a,int b){
arr[a]=arr[a]^arr[b];
arr[b]=arr[a]^arr[b];
arr[a]=arr[a]^arr[b];
}
void adjust(vector<int>& arr,int len,int index){
int maxid=index;
// 计算左右子节点的下标 left=2*i+1 right=2*i+2 parent=(i-1)/2
int left=2*index+1,right=2*index+2;
// 寻找当前以index为根的子树中最大/最小的元素的下标
if(left<len and arr[left]<arr[maxid]) maxid=left;
if(right<len and arr[right]<arr[maxid]) maxid=right;
// 进行交换,记得要递归进行adjust,传入的index是maxid
if(maxid!=index){
swap(arr,maxid,index);
adjust(arr,len,maxid);
}
}
void heapsort(vector<int>&arr,int len){
// 初次构建堆,i要从最后一个非叶子节点开始,所以是(len-1-1)/2,0这个位置要加等号
for(int i=(len-1-1)/2;i>=0;i--){
adjust(arr,len,i);
}
// 从最后一个元素的下标开始往前遍历,每次将堆顶元素交换至当前位置,并且缩小长度(i为长度),从0处开始adjust
for(int i=len-1;i>0;i--){
swap(arr,0,i);
adjust(arr,i,0);// 注意每次adjust是从根往下调整,所以这里index是0!
}
}
int main(){
vector<int> arr={3,4,2,1,5,8,7,6};
cout<<"before: "<<endl;
for(int item:arr) cout<<item<<" ";
cout<<endl;
heapsort(arr,arr.size());
cout<<"after: "<<endl;
for(int item:arr)cout<<item<<" ";
cout<<endl;
return 0;
}