前言:常用的排序算法主要包括冒泡排序、选择排序、插入排序、快速排序、希尔排序和归并排序
目录
一、冒泡排序
冒泡排序是排序算法中最为简单的排序之一,其思想就像水泡从水底一步一步冒到最上层,不断通过比较和交换来把小的数放到最前面。举个例子,对无序序列8,9,3,1,4,5进行冒泡排序
- 首先5和4作比较,由于4比5小,所以不用交换(8,9,3,1,4,5);4和1比较,由于1比4小,还是不用交换(8,9,3,1,4,5);1和3比较,由于1比3小,所以要交换(8,9,1,3,4,5);1和9比较(8,9,1,3,4,5);1和8比较(1,8,9,3,4,5)。第一轮比较完后结果为(1,8,9,3,4,5)。
- 同理进行第二轮比较,结果为(1,3,8,9,4,5)
- 第三轮结果为(1,3,4,8,9,5)
- 第四轮结果为(1,3,4,5,8,9)
- 第五轮结果为(1,3,4,5,8,9)
代码实现:
#include <stdio.h>
static void swap(int *arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void bubble(int *arr, int size)
{
int i = 0, j = 0;
for (i = 0; i < size - 1; i++)
{
for (j = size - 1; j > i; j--)
{
if (arr[j] < arr[j - 1])
{
swap(arr, j, j -1);
}
}
}
}
int main(int argc, char **argv)
{
int i = 0;
int arr[] = {8,9,3,1,4,5};
int size = sizeof(arr)/sizeof(arr[0]);
bubble(arr, size);
printf("result is ");
for (i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\r\n");
return 0;
}
二、选择排序
选择排序和冒泡排序比较接近,只是选择排序每次选择一个最小的值然后放到第一位,比冒泡好的地方就是减少了交换次数。举个例子,对无序序列8,9,3,1,4,5进行选择排序
- 第一轮选择一个最小值1,和8交换,结果为(1,9,3,8,4,5)
- 第二轮选择一个最小值3,和9交换,结果为(1,3,9,8,4,5)
- 第三轮选择一个最小值4,和9交换,结果为(1,3,4,8,9,5)
- 第四轮选择一个最小值5,和8交换,结果为(1,3,4,5,9,8)
- 第五轮选择一个最小值8,和9交换,结果为(1,3,4,5,8,9)
代码实现:
#include <stdio.h>
static void swap(int *arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static void select(int *arr, int size)
{
int i = 0, j = 0;
for (i = 0; i < size - 1; i++)
{
int min_idx = i;
for (j = i + 1; j < size; j++)
{
if (arr[j] < arr[min_idx])
{
min_idx = j;
}
}
if (min_idx != i)
{
swap(arr, i, min_idx);
}
}
}
int main(int argc, char **argv)
{
int i = 0;
int arr[] = {8,9,3,1,4,5};
int size = sizeof(arr)/sizeof(arr[0]);
select(arr, size);
printf("result is ");
for (i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\r\n");
return 0;
}
三、插入排序
插入排序类似对扑克牌进行排序,开始是会从第二张牌往前面找,找到可以插入的位置进行插入,然后拿第三张牌进行插入,同理第四张、第五张...。举个例子,对无序序列8,9,3,1,4,5进行插入排序
- 第一轮选择9作为需要插入的牌,往前找可以插入的地方,发现前面的8比自己小,所以不需要插入,结果为(8,9,3,1,4,5)
- 第二轮选择3作为需要插入的牌,往前找可以插入的地方,发现前面的8和9都比自己大,所以插到最前面,结果为(3,8,9,1,4,5)
- 第三轮选择1作为需要插入的牌,同理结果为(1,3,8,9,4,5)
- 第四轮选择4作为需要插入的牌,同理结果为(1,3,4,8,9,5)
- 第五轮选择5作为需要插入的牌,同理结果为(1,3,4,5,8,9)
代码实现:
#include <stdio.h>
static void insert(int *arr, int size)
{
int i = 0, j = 0;
for (i = 1; i < size; i++)
{
int wait_insert = arr[i];
int j = i;
while (j > 0 && (wait_insert < arr[j - 1]))
{
arr[j] = arr[j - 1];
j--;
}
if (j != i)
{
arr[j] = wait_insert;
}
}
}
int main(int argc, char **argv)
{
int i = 0;
int arr[] = {8,9,3,1,4,5};
int size = sizeof(arr)/sizeof(arr[0]);
insert(arr, size);
printf("result is ");
for (i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\r\n");
return 0;
}
四、快速排序
快速排序在实际应用中是表现最好的排序算法,快速排序首先会选择一个基准数,一般是第一个元素,然后左边和右边一起往中间搜索
- 首先右指针往左边搜索,当遇到比基准数小时,停止搜索
- 左指针往右边搜索,当发现有比基准数大的,停止搜索
- 然后把左边的值和右边的值交换。
- 然后重读1,2,3。直到左右指针相等,然后把当前值和基准值交换。
- 进过上面步骤,就把序列以基准数分成了两部分,然后再把左右部分再进行快速排序(递归)
- 最终完成排序。
举个例子,对无序序列8,9,3,1,4,5进行快速排序
第一轮(循环上面的1,2,3):(8,9,3,1,4,5)->(8,5,3,1,4,9)->(4,5,3,1,8,9)
第二轮把序列分成(4,5,3,1)和(9),再对这两个序列进行快速排序(4,5,3,1)->(4,1,3,5)->(3,1,4,5)
...
代码实现:
#include <stdio.h>
static void swap(int *arr, int i, int j)
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static int part(int *arr, int left, int right)
{
int base = arr[left];
int base_idx = left;
while(left < right)
{
while ((left < right) && (arr[right] >= base))
{
right--;
}
while ((left < right) && (arr[left] <= base))
{
left++;
}
swap(arr, left, right);
}
swap(arr, base_idx, left);
return left;
}
static void sort(int *arr, int left, int right)
{
if (left >= right)
{
return;
}
int pos = part(arr, left, right);
sort(arr, left, pos - 1);
sort(arr, pos + 1, right);
}
static void quick(int *arr, int size)
{
sort(arr, 0, size - 1);
}
int main(int argc, char **argv)
{
int i = 0;
int arr[] = {8,9,3,1,4,5};
int size = sizeof(arr)/sizeof(arr[0]);
quick(arr, size);
printf("result is ");
for (i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\r\n");
return 0;
}
五、希尔排序
希尔排序其实是插入排序的一种高效率体现,简单的插入排序中,如果序列是基本有序的,使用直接插入排序效率会很高,希尔排序实际就是利用这个特点,首先是待排序序列分割成若干子序列分别进行直接插入排序,使得整个序列基本有序,然后再对全体记录进行一次直接插入排序。
代码实现:
#include <stdio.h>
static void sort(int *arr, int size, int d)
{
int i = 0;
for (i = d; i < size; i++)
{
int wait_insert = arr[i];
int j = i - d;
while ((j >= 0) && (arr[j] > wait_insert))
{
arr[j + d] = arr[j];
j -= d;
}
if (j != (i - d))
{
arr[j + d] = wait_insert;
}
}
}
static void shell(int *arr, int size)
{
int d = size / 2;
while (d >= 1)
{
sort(arr, size, d);
d /= 2;
}
}
int main(int argc, char **argv)
{
int i = 0;
int arr[] = {23,64,24,12,9,16,53,57,71,79,87,97};
int size = sizeof(arr)/sizeof(arr[0]);
shell(arr, size);
printf("result is ");
for (i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\r\n");
return 0;
}
六、归并排序
归并排序首先将无序序列进行划分,不可分割后就行排序重组,就是两两合并,然后四四合并,...最终成为有序序列。举个例子,对无序序列(23,64,24,12,9,16,53,57,71,79,87,97)进行合并排序。
如下图所示:
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void sort(int *arr, int left, int mid, int right)
{
int i = left;
int j = mid + 1;
int k = 0;
int *temp = (int *)malloc((right - left + 1) * sizeof(int));
if (NULL == temp)
{
return;
}
memset(temp, 0, (right - left + 1) * sizeof(int));
while ((i <= mid) && (j <= right))
{
if (arr[i] <= arr[j])
{
temp[k++] = arr[i++];
}
else
{
temp[k++] = arr[j++];
}
}
while (i <= mid)
{
temp[k++] = arr[i++];
}
while (j <= right)
{
temp[k++] = arr[j++];
}
for (k = 0; k < (right - left + 1); k++)
{
arr[left + k] = temp[k];
}
free(temp);
}
static void part(int *arr, int left, int right)
{
if (left >= right)
{
return;
}
int mid = (left + right)/ 2;
part(arr, left, mid);
part(arr, mid + 1, right);
sort(arr, left, mid, right);
}
static void merge(int *arr, int size)
{
part(arr, 0, size - 1);
}
int main(int argc, char **argv)
{
int i = 0;
int arr[] = {23,64,24,12,9,16,53,57,71,79,87,97};
int size = sizeof(arr)/sizeof(arr[0]);
merge(arr, size);
printf("result is ");
for (i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\r\n");
return 0;
}
总结
下面总结一下各排序的复杂度,如下图所示