一、直接插入排序
1、基本思想:对于数组a[1...n],初始时,a[1]为一个有序区间,a[2...n]为无序区间,然后依次将a[2],a[3].....a[n]插入到有序区间内。
2、代码
/*直接插入排序*/
void InsertSort(int arr[], int length)
{
for (int i = 1; i < length; i++)
{
int temp = arr[i];
int j;
for (j = i - 1; j >=0 && temp<arr[j] ; j--)//从后往前进行比较
{
arr[j+1] = arr[j];
}
arr[j + 1] = temp;
}
}
3、稳定、时间复杂度O(n^2) 空间复杂度o(1)
二、希尔(shell)排序
1、基本思想:改进的的插入排序,首先将待排序列按照某个增量d分成几个组,每个组中记录下标相差d对每个组中的元素进行排序,然后减小d重新排序,直至d为1,整个序列变成一个组,排序完成。
2、代码
/*希尔排序(d开始为序列的一半,每次减半)*/
void ShellSort(int arr[], int length)
{
for (int d = length / 2; d > 0; d = d / 2)
{
for (int i = 1; i < length; i++)
{
int temp = arr[i];
int j;
for (j = i - d; j >= 0 && arr[j]>temp; j-=d)
{
arr[j + d] = arr[j];
}
arr[j + d] = temp;
}
}
}
3、 不稳定 时间复杂度O(nlog2n) 空间复杂度o(1)
三、冒泡排序
1、基本思想:对于待排序列a,从头到尾扫描,将较大的数交换至最后的位置,直到整个序列有序
2、代码:
void Swap(int &a, int &b)//“或”实现两个数的交换
{
a = a^b;
b = b^a;
a = a^b;
}
/*直接插入排序*/
void BubberSort(int arr[], int length)
{
for (int i = 0; i < length-1; i++)
{
int exchange = 0; //添加变量记录本次遍历有无交换,若无已经有序,跳出循环
for (int j = i + 1; j < length; j++)
{
if (arr[i]>arr[j])
{
swap(arr[j], arr[i]);
exchange = 1;
}
}
if (exchange != 1)
return;
}
}
3、稳定 时间复杂度O(n^2) 空间复杂度o(1)
四、快速排序
1、基本思想:采用分治法,将原问题分解为若干与原问题结构相似的子问题,递归解决。首先,选取关键字pivot将待排序列分为两个部分,一部分元素全部大于pivot,另一部分全部小于pivot,pivot的位置得到确定。然后再递归的对小于和大于pivot的序列进行快排。
2、代码
/*快速排序*/
void QuickSort(int arr[],int first,int last)
{
int low = first;
int high = last;
int pivot = arr[low];
if (low < high)
{
while (low < high)
{
while (low < high && arr[high] >= pivot)//注意先从高位开始比较
{
high--;
}
if (low < high)
arr[low++] = arr[high];
while (low < high && arr[low] <= pivot)
{
low++;
}
if (low < high)
arr[high--] = arr[low];
}
arr[low] = pivot;
QuickSort(arr, first, low - 1);
QuickSort(arr, low + 1, last);
}
}
3、不稳定 时间复杂度o(nlog2n) 由于采用了递归空间复杂度o(logn)
五、选择排序
1、基本思想:在待排序列的无序区间的a[i+1.....n]中,选择一个最小的数跟a[i] 交换
2、代码:
/*选择排序*/
void SelectSort(int arr[], int length)
{
for (int i = 0; i < length-1; i++)
{
int min = arr[i];
int index = i;
for (int j = i+1; j < length; j++)
{
if (arr[j] < min)
{
min = arr[j];
index = j;
}
}
arr[index] = arr[i];
arr[i] = min;
}
}
3、不稳定 时间复杂度 O(n^2) 空间复杂度O(1)
六、堆排序
1、基本思想:堆排序有两种大根堆(所有子节点都小于其父节点,即Ai<=A2i且Ai<=A2i+1),小根堆(所有子节点都小于其父节点,即Ai>=A2i且Ai>=A2i+1)。
2、代码
以大根堆为例,首先将待排数列建成一个堆
void Built_Maxheap(int arr[], int length)//建立堆
{
heapSize = length-1;
for (int i = (length >> 1-1); i >= 0 ; i--) //n/2-1之前都是父节点,之后的节点都是子节点,只需要对父节点进行maxHeapify
{
maxHeapify(arr, i);
}
}
然后再将最大关键字堆顶元素与无序区的最后一个元素交换
void MaxHeapSort(int arr[], int length)//堆排序
{
Built_Maxheap(arr, length);
for (int i = length - 1; i >= 1; i--)
{
swap(arr[0], arr[i]);
heapSize--;
maxHeapify(arr, 0);
}
}
交换过后可能会导致新的根违反大根堆的定义,所以要进行调整
void maxHeapify(int arr[], int index)//堆的调整
{
int max = index;
int left = Left(index);
int right = Right(index);
if (left <= heapSize && arr[left] > arr[index])
max = left;
if (right <= heapSize && arr[right] > arr[max])
max = right;
if (max != index)//如果堆顶不是最大者,则交换,并递归调整堆
{
Swap(arr[index], arr[max]);
maxHeapify(arr, max);
}
}
其中,根节点的左右子树计算方法如下
void Swap(int &a, int &b)//用“^”完成交换操作
{
a = a^b;
b = b^a;
a = a^b;
}
int Left(int i){ return ((i << 1) + 1); } //leftchild=2*i+1;返回左子节点索引
int Right(int i){ return ((i << 1) + 2); }//rightchild=2*i+2返回右子节点索引
int heapSize = 0;
3、不稳定 时间复杂度建堆O(n),堆调整O(nlogn),所以为O(nlogn) 空间复杂度O(1)
七、归并排序
1、基本思想:假设有两个有序的子文件,放在A[low......m],A[m+1......n]上,先将他们合并到一个Temp中,待排序完毕后在复制到原数组中。
2、代码(自顶向下)
//将两个子序列按照大小顺序填入临时数组temp中
void Merge(int arr[], int temp[], int start1, int end1, int end2)
{
int start2 = end1 + 1;
int tempindex = start1;
int number = end2 - start1 + 1;
while (start1<=end1 && start2<=end2)
{
if (arr[start1] <= arr[start2])
{
temp[tempindex++] = arr[start1];
start1++;
}
else{
temp[tempindex++] = arr[start2];
start2++;
}
}
while (start1 <= end1)
{
temp[tempindex++] = arr[start1];
start1++;
}
while (start2<=end2)
{
temp[tempindex++] = arr[start2];
start2++;
}
for (int i = 0; i < number; i++, end2--)
arr[end2] = temp[end2];
}
void merge_sort(int arr[], int temp[], int start, int end)
{
if (start >= end)
return;
int middle = (start + end) / 2;//分裂节点
merge_sort(arr, temp, start, middle);//分别递归排序
merge_sort(arr, temp, middle + 1, end);
Merge(arr, temp, start, middle, end);//合并
}
/*归并排序*/
void MergeSort(int arr[], int length)
{
int *temp = NULL;
temp = new int[length];
if (temp != NULL)
{
merge_sort(arr, temp, 0, length - 1);
delete[] temp;
}
}
3、稳定,时间复杂度O(nlogn) 空间复杂度O(n)
八、基数排序
1、基本思想:从低位到高位依次对数据进行排序,第d趟排序就是对第d位进行排序,所需箱子数就是技术rd。
2、代码
int find_max(int arr[], int length)//找到最大的一个数
{
int max = arr[0];
for (int i = 1; i < length; ++i)
{
if (arr[i]>max)
max = arr[i];
}
return max;
}
int digitnumber(int number)//求数字的位数
{
int num = 0;
while (number > 0)
{
number = number / 10;
num++;
}
return num;
}
int kdigitnumber(int number,int k)//求第k位的元素,从个位数开始计数
{
number = number / pow(10, k);
return number % 10;
}
/*基数排序*/
void RadixSort(int arr[], int length)
{
int *temp[10];//指针数组,指向10个桶
int max = find_max(arr,length);
int maxDigit = digitnumber(max);
int count[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };//每个桶元素的个数
for (int i = 0; i < 10; ++i)//给桶分配空间,并初始化
{
temp[i] = new int[length];
memset(temp[i], 0, sizeof(int)*length);
}
for (int i = 0; i < maxDigit; i++)
{
memset(count, 0, sizeof(int)* 10);
for (int j = 0; j < length; j++)
{
int x = kdigitnumber(arr[j], i);
temp[x][count[x]] = arr[j];
count[x]++;
}
int index = 0;//对桶中的元素进行收集操作
for (int k = 0; k < 10; k++)
{
for (int m = 0; m < count[k]; m++)
{
arr[index++] = temp[k][m];
}
}
}
}
3、不稳定 时间复杂度O(kn) 空间复杂度O(n)(其中k为数字的最大位数)