数据结构排序算法
排序:排序即排列顺序,把一组数据排列成有序的数据,或者把一组数据按照关键字排成有序的
排序的分类:
(1)按照存储位置分为:内部排序(即在内存中)和外部排序(即内存调用磁盘)
(2)按照算法或者逻辑分为:插入排序、选择排序、交换排序、归并排序和基数排序
(3)按照排序结果分为:升序排序和降序排序
时间复杂度:某个事件的执行次数
空间复杂度:临时存储位的个数
稳定性:重复数据在排序前后的位置不发生变化为稳定,反之为不稳定
下面将简要介绍插入排序、选择排序、交换排序、归并排序和基数排序等排序算法
插入排序(直接插入排序和希尔排序)
直接插入排序:
(1)把一个数组先划分成已排序的部分和未排序的部分
(2)从未排序部分获取一个关键字作为待排序数
(3)在已排序的部分中找到合适的位置插入这个数据
直接插入排序的时间复杂度为 O(n^2), 空间复杂度为 O(1), 该算法是稳定的排序算法
直接插入排序代码如下:
void insertSort(int arr[], int len)
{
int i, j;
for (i = 2; i < len; i++)
{
arr[0] = arr[i];
for (j = i - 1; arr[j] > arr[0]; j--)
{
arr[j + 1] = arr[j];
}
arr[j + 1] = arr[0];
}
}
希尔排序:即在直接插入排序的基础上,增加一个增量组合
希尔排序的时间复杂度为 O(n^1.3) ~ O(n^1.5), 空间复杂度为 O(1), 该算法是不稳定的排序算法
希尔排序的代码如下:
void shell(int *arr, int arr_len,int dk)
{
int i, j;
int tmp;
for (i = dk; i < arr_len; i++)
{
tmp = arr[i];
for (j = i - dk; j >= 0 && arr[j] > tmp; j = j - dk)
{
arr[j + dk] = arr[j];
}
arr[j + dk] = tmp;
}
}
void shellSort(int *arr, int arr_len, int *dka, int dka_len)
{
for (int i = 0; i < dka_len; i++)
{
shell(arr,arr_len,dka[i]);
}
}
选择排序(简单选择排序和堆排序)
简单选择排序(以升序为例):一趟排序中找到最小的数,二趟排序中找到剩下数列中最小的数
简单选择排序是一种不稳定的排序算法
简单选择排序的代码如下:
void SelectSort(int *arr, int len)
{
int i, j;
int min;
int tmp;//O(n^2) O(1)
for (int i = 0; i < len-1; i++)
{
min = i;
for (j = i + 1; j < len; j++)
{
if (arr[j] < arr[min])
{
min = j;
}
}
tmp = arr[min];
arr[min] = arr[i];
arr[i] = tmp;
}
}
void HeapAdjust(int *arr, int i, int len)//堆调整
{
int j;
//j <= len 有左子树
for (j = 2 * i; j <= len; j = 2 * j)
{
//j 左子树 j = len; //有左 没右 j = len
//左右都有
//就j < len
//j = len 有左 没右
// j < len 左右都有
if (j < len && arr[j] < arr[j + 1])
{
j++;
}
if (arr[j] < arr[i])break;
arr[0] = arr[i];
arr[i] = arr[j];
arr[j] = arr[0];
i = j;
}
}
void HeapSort(int *arr, int len)//堆排序
{
int tmp;
for (int i = len / 2; i > 0; i--)
{
//i 当前要调整的堆的父节点下标 len 有效长度
HeapAdjust(arr,i,len);
}
for (int j = len; j > 0; j--)
{
tmp = arr[1];
arr[1] = arr[j];
arr[j] = tmp;
HeapAdjust(arr, 1, j-1);
}
}
交换排序(冒泡排序和快速排序)
冒泡排序代码如下:
void BubbleSort(int *arr, int len)
{
int tmp;
bool mark = false;
for (int i = 0; i < len-1; i++)
{
mark = false;
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j]>arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
mark = true;
}
}
printf("i = %d\n", i);
if (!mark)
{
break;
}
}
}
快速排序(分治法):选一个基准数,小的数据放在这个数左边,大的数据放在这个数右边
快速排序的代码如下:
int partition(int *arr, int low, int high)
{
int tmp = arr[low];
while (low < high)
{
while (low < high && arr[high] >= tmp) high--;
arr[low] = arr[high];
while (low < high && arr[low] <= tmp) low++;
arr[high] = arr[low];
}
arr[low] = tmp;
return low;
}
void QSort(int *arr, int low, int high)
{
if (low < high)
{
int boundKey = partition(arr,low, high);
QSort(arr, low, boundKey - 1);
QSort(arr, boundKey+1, high);
}
}
void QuickSort(int *arr, int len)
{
QSort(arr, 0, len - 1);
}
归并排序
(1)分:利用二分法划分数列成小组
(2)合:小组按照二分法划分出的小组组合
归并排序的代码如下:
void Merge(int *arr, int *tmp, int startIndex, int midIndex, int endIndex)
{
int i = startIndex;
int j = midIndex + 1;
int k = startIndex;
while (i != midIndex + 1 && j != endIndex + 1)
{
if (arr[i] > arr[j])
{
tmp[k++] = arr[j++];
}
else
{
tmp[k++] = arr[i++];
}
}
while (i != midIndex + 1)
{
tmp[k++] = arr[i++];
}
while (j != endIndex + 1)
{
tmp[k++] = arr[j++];
}
for (int i = startIndex; i <= endIndex; i++)
{
arr[i] = tmp[i];
}
}
void MergeSort(int *arr, int *tmp, int startIndex, int endIndex)
{
if (startIndex < endIndex)
{
int midIndex = (startIndex + endIndex) / 2;
MergeSort(arr, tmp, startIndex, midIndex);
MergeSort(arr, tmp, midIndex + 1, endIndex);
Merge(arr, tmp, startIndex, midIndex, endIndex);
}
}
基数排序(排序趟数为最大值的位数)
基数排序的代码如下:
#define N 13
//double pow(double,int);
int FindMaxFinger(int *arr, int len)//计算最大值的位数
{
int max = arr[0];
for (int i = 1; i < len; ++i)
{
if (arr[i] > max)
{
max = arr[i];
}
}
int count = 0;
while (max != 0)
{
max = max / 10;
count++;
}
return count;
}
//每个数据位的数 (45 0) 5 (45 1) 4
//num/pow(10.0,fin)%10
int FindFingerNumber(int num, int fin)
{
return num / (int)pow(10.0,fin) % 10;
}
//0,1,2(个,十,百) 0不能参与排序
void Radix(int *arr, int len, int fin)
{
int tmp[10][N] = {};
int num_fin;
int count;
for (int i = 0; i < len; i++)
{
count = 0;
num_fin = FindFingerNumber(arr[i], fin);//每个数据位的数
while (tmp[num_fin][count] != 0)
{
count++;
}
tmp[num_fin][count] = arr[i];
}
count = 0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < len; j++)
{
if (tmp[i][j] != 0)
{
arr[count++] = tmp[i][j];
}
else
{
break;
}
}
}
}
//0可以参与排序
void Radix1(int *arr, int len, int fin)
{
int tmp[10][N] = {};
int num_fin;
int count[10] = {};
for (int i = 0; i < len; i++)
{
num_fin = FindFingerNumber(arr[i], fin);
tmp[num_fin][count[num_fin]] = arr[i];
count[num_fin]++;
}
int index = 0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < count[i]; j++)
{
arr[index++] = tmp[i][j];
}
}
}
//每趟循环调用Radix()
void RadixSort(int *arr, int len)
{
int maxFinNum = FindMaxFinger(arr, len);
for (int i = 0; i < maxFinNum; i++)
{
Radix1(arr, len, i);
}
}
以上算法的时间复杂度、空间复杂度以及稳定性如下表显示: