快速排序
快速排序算法基本思想:
- 1.先从数列中取出一个数作为基准数。
- 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
- 3.再对左右区间重复第二步,直到各区间只有一个数。
#include <stdio.h>
#define size 6
//快速排序
void quick_sort(int num[], int left, int right )//low为要排序的数组的左下标,high为右下标
//num为要排序的数组
{
int i = left, j = right, base_num;
base_num = num[left]; //任命为中间分界线(基准数),左边比他小,右边比他大,通常第一个元素是基准数
if (i > j) { //如果下标i大于下标j,函数结束运行
return;
}
while (i != j) { //两个哨兵一直往中间靠拢直到相遇
/*
例如:
对数组元素:5 6 7 2 6 5 4
下标:0 1 2 3 4 5 6
此时:low=0,high=6
第一次进入while循环:i=0,j=6,base_num=5
右边哨兵经移动后:j=6
左边:i=1
进行交换后数组:5 4 7 2 6 5 6
下标:0 1 2 3 4 5 6
第二次进入while循环:i=0,j=6,base_num=5
右边哨兵经移动后:j=3
左边:i=2
进行交换后数组:5 4 2 7 6 5 6
下标:0 1 2 3 4 5 6
第二次进入while循环:i=0,j=6,base_num=5
右边哨兵经移动后:j=2
左边:i=2
不交换,结束循环
*/
while (num[j] >= base_num && j > i) { //先看右边的哨兵,若右边哨兵比基准数大
j--;//右边哨兵往中间移动一个单位
}
//右哨兵往中间移动直到找到一个数组元素比基准数小,不能与基准数相等
while (num[i] <= base_num && j > i) { //若左边哨兵比基准数小
i++;//左边哨兵往中间移动一个单位
}
//左哨兵往中间移动直到找到一个数组元素比基准数大,不能与基准数相等
if (j > i) {//左右哨兵没有越界,左哨兵在左,右哨兵在右
int temp;//将左右哨兵此时所指的数组元素互换位置
temp = num[j];
num[j] = num[i];
num[i] = temp;
}
}
num[left] = num[i];//将基准数放到两个哨兵中间
num[i] = base_num;
/*
对数组元素:5 4 2 7 6 5 6
下标:0 1 2 3 4 5 6
交换两个元素后:
对数组元素:2 4 5 7 6 5 6
下标:0 1 2 3 4 5 6
此时i=2,j=2
*/
quick_sort(num, left, i - 1);
/*
对数组元素:2 4 5 7 6 5 6
下标:0 1 2 3 4 5 6
其中的0,1下标的元素排序
*/
quick_sort(num, i + 1, right);
/*
对数组元素:2 4 5 7 6 5 6
下标:0 1 2 3 4 5 6
其中的3,6下标的元素排序
*/
}
int main() {
//创建一个数组
int num[size] = {0};
int i;
//输入数字
for (i = 0; i < size; i++) {
scanf("%d", &num[i]);
}
quick_sort(num, 0, size - 1);
for (i = 0; i < size; i++) {
printf(" %d ", num[i]);
}
return 0;
}
过程:
首先记录下base_num的位置,然后left和right分别从数组两端开始往中间走。
right先开始向中间行动(要让right指针先行动,因为这样一定要能保证相遇的地方比base_num处的值要小。相遇位置就是R停下来的位置,好的情况是right处的值比base_num处的小,最坏的情况就是right走到了base_num的位置,那此时交换也没有影响。),如果right处的值小于base_num处的值,则停止等待left走。
left开始行动,当left找到大于base_num处的值时(找值时,left或right处的值一定要比base_num处的小(大),等于也不行,如果出现以下这种情况会死循环。),left和right处的值进行交换。
当两个位置相遇时,将相遇位置的值与base_num处的值进行交换,并将相遇的位置置为新的base_num
插入排序
插入排序算法基本思想:
每次将一个待排序的记录按其关键字大小插入前面已排列好的子序列(通常一开始是默认为第一个元素为有序序列),直至全部记录插入完成。
#include<stdio.h>
void InsertSort(int* a, int n) {
for (int i = 0; i < n - 1; i++) { //用i来调整end
int end = i;//已排序序列[0, end]
int tmp = a[end + 1];//向已排序序列中插入数据a[end + 1],先将未排序数据a[end + 1]保存到tmp
while (end >= 0) { //在已排序序列中从后向前扫描,将比tmp大的数向后移动,直到找到合适的插入位置时,退出循环
/*
例如:
对数组:7 8 9 5 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=2,tmp=5
*/
if (tmp < a[end]) { //如果tmp<a[end]则a[end]向后移动(升序)
/*
进入该语句:
对数组:7 8 9 5 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=2,tmp=5
交换后:
对数组:7 8 9 9 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=2,tmp=5
第二次进入该语句:
对数组:7 8 9 9 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=1,tmp=5
交换后:
对数组:7 8 8 9 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=1,tmp=5
第三次进入该语句:
对数组:7 8 8 9 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=0,tmp=5
交换后:
对数组:7 7 8 9 6 1 2 3
下标:0 1 2 3 4 5 6 7
此时:end=0,tmp=5
*/
a[end + 1] = a[end];
end--;
} else {
break;//因为[0,end]是有序序列,如果tmp>=a[end],则将tmp插入到end+1位置即可
}
}
a[end + 1] = tmp;//退出循环后,将tmp插入找到的合适的位置
/*
插入后:
对数组:5 7 8 9 6 1 2 3
下标:0 1 2 3 4 5 6 7
*/
}
}
int main() {
int a[10] = {45, 12, 6, 23, 43, 42, 12, 61, 27, 45,};
InsertSort(a, 10);
for (int i = 0; i < 10; i++) {
printf("%d ", a[i]);
}
}
/*
优化:
1.折半插入排序
2.希尔排序
*/
二分查找
对于数组查找元素:
要求:必须是有序数组
过程:假设数组元素按升序排列,将数组从中间拆分为前后两个数组,先将查找元素与数组中间元素进行比较,若查找元素大于中间元素,继续查找后数组,若查找元素小于中间元素,继续查找前数组,重复上述过程,当数组只剩一个元素,看数组中间元素是否与查找元素相等,若相等,返回中间元素下标,不相等则返回0;
int cz(int arr[], int left, int right, int key) {
int mid = ( left + right ) / 2;
if (key == arr[mid]) {
return mid;//返回要查找元素的下标
}
if (key < arr[mid]) {
return cz(arr, left, mid - 1, key);
}
if (key > arr[mid]) {
return cz(arr, mid + 1, right, key);
} else {
return 0;
}
}
//递归
int zb(int key, int a[], int n) { //key表示要找的数,a表示数组,n表示数组元素个数
int high, low, mid;
low = 0;
high = n - 1;
while (high >= low) {
mid = (high + low) / 2;
if (key < a[mid])
high = mid - 1;
else if (key > a[mid])
low = mid + 1;
if (key == a[mid]) {
return mid;//返回要查找元素的下标
break;
}
}
return 0;
}
//非递归