目录
BubbleSort
// 由小到大
void BubbleSort( int arr[], int n )
{
for (int i = 0; i < n; i++)
{
for (int j = n-1; j > i; j--)
{
if (arr[j-1] > arr[j])
{
swap( arr[j-1], arr[j]);
}
}
}
}
InsertSort
// 找到当前数据合适插入位置进行插入
void InsertSort( int arr[], int n )
{
int i, j;
// 第一个元素相当于已经排好,从第二个元素开始
for (i = 1; i < n; i++)
{
int tmp = arr[i];
// 从i之前的元素开始倒序比较
for ( j = i-1; j >= 0 && arr[j] > tmp; j--)
{
arr[j+1] = arr[j];
}
// arr[j] <= tmp 跳出循环, 所以arr[i]的位置为j+1
// arr[i] 插入到j+1索引位置
arr[j+1] = tmp;
}
}
二分排序
二分排序就是折半插入排序,当直接插入排序进行到某一趟时,对于前面记录已经按关键字有序,此时不用直接插入排序的方法,而用折半二分查找,找出下一个元素应插入的位置,然后插入,这种方法就是折半插入排序,这种方法中比较次数,由于采用折半查而减少,为O(nlogn),但是元素交换的次数仍为O(n2),二分排序算法是稳定的。
void binary_sort(int a[],int n)
{
int low ,high, mid;
int tem;
for(int i=1; i<n; i++)
{
tem=a[i];
low=0;
high=i-1;
while(low<=high)
{
mid=(low+high)/2;
if(a[mid]>tem)
{
high=mid-1;
}
else
{
low=mid+1;
}
}
for(int j=i-1; j>high; j--)
{
a[j+1]=a[j];
}
a[high+1]=tem;
}
}
选择排序
// 从[i...n-1]中找到最小的值,放到arr[i]的位置
void SelectionSort(int arr[], int n)
{
int i, j;
for ( i = 0; i < n; i++)
{
//将当前值作为最小值,索引值保存在minIndex中
int minIndex = i;
for ( j = i+1; j < n; j++)
{
if (arr[j] < arr[minIndex])
{
minIndex = j;
}
}
if (minIndex != i)
{
swap(arr[i], arr[minIndex]);
}
}
}
HillSort
// 希尔排序,是插入排序的改进,它将一个数组按一定间隔分成几部分进行插入排序
void HillSort(int arr[], int n)
{
int increament = n;
int i, j;
do
{
increament = increament/3+1;
// i初始值从increament开始,是从每组数据的第二个开始
// 因为每组数据第一个相当与已经排好序了
for( i = increament; i < n; i++)
{
int tmp = arr[i];
// j = i-increament 是同一组i索引的前一个元素
for( j = i-increament; j >= 0 && tmp < arr[j]; j-=increament)
{
arr[j+increament] = arr[j];
}
// 插入位置应该为j,但是for循环中最后计算了一次j-=increament
arr[j+increament] = tmp;
}
}
while (increament > 1);
}
快速排序(递归)
// 选择一个pivot,将数组分成两段,一段比pivot小,一段比pivot大
void QuickSort( int arr[], int n)
{
QSort( arr, 0, n-1);
}
void QSort(int arr[], int low, int high)
{
if (low >= high) return;
// 将arr一分为二 [low... pivot-1], [pivot+1...high]
int pivot = Partition( arr, low, high );
QSort( arr, low, pivot-1 );
QSort( arr, pivot+1, high);
}
// Partition实现一(推荐)
// 将arr 以pivotkey 一分为二
// v = arr[low] [low+1...j] < v [j+1...high) > v
int Partition( int arr[], int low, int high){
swap( arr[low] , arr[rand()%(high - low + 1)+low] );//随机取数作为轴
int v = arr[low];
int j = low; // 左半部(比v小)最后一个数索引
//i = low+1 开始遍历,arr[i] > v,++i;
// <v,将其和第一个比v大的数(arr[j+1])交换, 再++j,++i,则 左半部分增添了一个数
// 最后再swap(arr[low], arr[j]) 将v交换至分隔位置
for (int i = low+1; i <= high; ++i){
if (arr[i] < v){
swap( arr[j+1], arr[i]);
++j;
}
}
swap( arr[low], arr[j] );
return j;
}
// 将arr 以pivotkey 一分为二
int Partition( int arr[], int low, int high)
{
int pivotkey = arr[low];
int tmp = pivotkey;
while (low < high)
{
while (low < high && pivotkey <= arr[high])
high--;
//swap( arr[low], arr[high]);
// 可以不使用交换,因为从最开始的tmp = pivotkey,已经将每个要交换的值进行了备份
// ar[low] = arr[high]将arr[high]已经备份到arr[low]中,每一次覆盖前都有备份
arr[low] = arr[high];
while (low < high && pivotkey >= arr[low])
low++;
//swap( arr[low], arr[high]);
arr[high] = arr[low];
}
arr[low] = tmp;
return low;
}
快速排序(非递归)(使用栈模拟)
void QSort( int arr[], int left, int right ){
if (left >= right)
return ;
stack<int> s;
s.push(left);
s.push(right); // right后入栈, 所以后边先出栈
while (!s.empty()){
int r = s.top();
s.pop();
int l = s.top();
s.pop();
int index = Partition(arr, l, r); // 划分 ; 之后继续将划分每一部分
if (l < index-1){ // 左子序列
s.push(left);
s.push(index-1);
}
if (index+1 < r){ // 右子序列
s.push(index+1);
s.push(r);
}
}
}
归并排序(递归)
void MergeSort( int arr[], int n)
{
MSort( arr, 0, n-1 );
}
void MSort( int arr[], int low, int high )
{
if (low >= high)
return;
int middle = low + (high-low)/2; // (low+high)/2
MSort( arr, low, middle );
MSort( arr, middle+1, high );
Merge( arr, low, middle, high);
}
// 将两个有序数组归并
// arr[low...middle] arr[middle+1 ... high]
// 赋值为 tmp[0...high-low]
void Merge( int arr[], int low, int middle, int high )
{
int tmp[high-low+1];
// tmp 数组想对于arr的索引有一个向左的low偏移
for (int i = low; i <= high; i++)
{
tmp[i-low] = arr[i];
}
// 两个索引,分别指向左半部开始 和 右半部开始
int i = low, j = middle+1;
for (int k = low; k <= high; k++)
{
if (i > middle)
{
arr[k] = tmp[j-low];
j++;
}
else if (j > high)
{
arr[k] = tmp[i-low];
i++;
}
else if (tmp[i-low] < tmp[j-low])
{
arr[k] = tmp[i-low];
i++;
}
else
{
arr[k] = tmp[j-low];
j++;
}
}
}
归并排序(迭代)(自底向上)
//自底向上实现:最先归并的时候,每段长度最小,都为1; 归并之后每段长度变成2
//归并一次,长度加倍
//归并的范围从[0..sz-1]与[sz+1, 2*sz-1]归并,[2*sz, 3*sz-1]与[3*sz, 4*sz-1]...
void MergeSort( int arr[], int n ){
for (int sz = 1; sz <= n; sz += sz){
for (int i = 0; i + sz < n; i += sz + sz){
// 对 arr[i...i+sz-1] 和 arr[i+sz...i+2*sz-1]进行归并
// i + sz < n保证i+sz-1在n的范围内
// min( i+sz+sz-1, n-1 ) 最后一部分可能i+sz+sz-1超过了n-1,只需归并到n-1即可
Merge( arr, i, i+sz-1, min( i+sz+sz-1, n-1) );
}
}
}
堆排序
// 1. 先建堆(大顶堆,或者小顶堆)
// 2. 在将根节点值与最后位置(层序遍历)交换
void HeapSort( int arr[], int n )
{
// n/2-1 是最后一个非叶子节点(有孩子)的索引
// 对该节点及孩子进行调整,构成大顶堆
// 对每一个叶子节点遍历进行操作调整
for (int i = n/2-1; i >= 0; i--)
{
HeapAdjust( arr, i, n);
}
for (int i = n-1; i >= 0; i--)
{
// 交换一次,堆的节点个数减少一个,所以为 i
swap( arr[0], arr[i] );
// 将arr[0...i-1] 重新调整为大顶堆
// 这里不需要和初始建堆一样加一个循环,是因为整个数组已经有序,
// 只需对相应的节点从上到下调整即可
HeapAdjust( arr, 0, i);
}
}
//将arr[s...n-1]调整为大顶堆 n指arr数组长度
void HeapAdjust( int arr[], int s, int n)
{
for (int j = 2*s+1; j < n; j=2*j+1)
{
// 右孩子比左孩子大,更新j,指向右孩子索引
// j+1<n 右孩子索引在范围内
if (j+1 < n && arr[j] < arr[j+1])
{
j++;
}
// 根节点值比左右孩子节点都大,不用交换
if (arr[s] > arr[j])
{
break;
}
//将跟节点值与左右孩子中较大的交换
swap( arr[s], arr[j] );
// s指向
s = j;
}
}