排序算法
-
1. 概念
- 把一组数字按照某种指定的顺序排列好
-
共性
- 排序算法通常需要分很多次重复进行, 每次只负责处理一个数字
- 为了处理一个数字可以有两种做法
- 首先选择一个数字然后根据这个数字去找他应该放在那个位置
- 首先选择一个位置然后根据这个位置去找适合放在这个位置里的数字
- 通过不断调整两个数字间的顺序最终把一个数字放在合适的位置里
-
2. 冒泡排序算法
- 从所有数字的两端选择一个位置作为选定位置
- 从选定位置的另一端开始不断对两个相邻数字进行顺序调整, 最终把合适的数字放在选定位置里
-
3. 选择排序算法
- 从所有数字的两端选择一个位置作为选定位置
- 用选定位置里的数字和所有其他数字进行顺序调整最终把合适的数字放在选定位置里
- 冒泡排序是把两个相邻的数字进行调整, 这是他们的主要区别
-
4. 插入排序算法
- 假设前面一组数字(或者第一个数字)已经排好顺序, 把后面相邻的数字向前插入到合适的位置
- 插入时每次向前移动一步
- 把正确的顺序不断扩大
-
5. 快速排序
- 把排序过程分为三步
-
- 1. 从所有数字两端选择一个数字作为基准数字,然后把所有比基准数字小的数字放在它左边,把所有比基准数字大的放在它的右边(这个时候基准数字就被放在了合适的位置里了)
-
- 2. 对左半边数字单独进行排序(不考虑右半边数字)
-
- 3. 对右半边数字单独进行排序(不考虑左半边数字)
- 第1步的实现:把基准数字和最另外一端的未处理数字进行调整,调整后把其中的非选定数字排除在两边,重复这个过程就可以完成第一步
fast sort
快速排序第一步的方法:选择最右边的30作为基准数字, 然后把所有比30小的都放在它左边, 比30大的都放在它右边
70 80 90 10 20 30
1. 把基准数字30和另外一端没处理过的数字70做顺序调整
↓
30 80 90 10 20 70
现在30和70的顺序符合要求, 以后只考虑 30 80 90 10 20 这个范围里的数字, 再不考虑70
因为以后不管30怎么移动位置, 70只会在30的右边
2. 下面再用30和另外一端没有处理过的数字20,做类似处理
↓
20 80 90 10 30 70
以后只处理 80 90 10 30 这个范围里的数字, 不在考虑20
3. 再用30和另外一端没有处理过的数字80,做类似处理
↓
20 30 90 10 80 70
以后只处理 30 90 10 这个范围里的数字,不再考虑80
4. 再用30和另外一端没有处理过的数字10,做类似处理
↓
20 10 90 30 80 70
↓
20 10 30 90 80 70
5. 下面对左半边数字进行单独排序
...
↓
10 20 30 70 80 90
#include <stdio.h>
//冒泡排序函数
void bubble_sort (int *p_val, int size) {
int num = 0, num1 = 0, tmp = 0;
//重复(size-1)次就可以把(size-1)个数字都放在合适的位置上, 剩下最后一个数字一定在合适的位置(num == 0)
for (num = size -1; num >= 1; num--) {
//循环变量代表选定位置的下标
//每次循环都要给选定位置填上合适的数字
for (num1 = 0; num1 <= num -1; num1++) { // 在选定位置不变的情况下, 从前向后的对两个相邻数字进行调整. 最终把合适的数字放在选定位置里
//每次循环都, 如果下标是num1的存储区里的数字比下表是(num1+1)的大, 那两个存储区里的内容做交换
if (*(p_val + num1) > *(p_val + num1 + 1)) {
tmp = *(p_val + num1);
*(p_val + num1) = *(p_val + num1 + 1);
*(p_val + num1 + 1) = tmp;
}
}
}
}
// 选择排序函数
void choice_sort(int *p_val, int size) {
int num = 0, num1 = 0, tmp = 0;
//外循环和冒泡一样,都是总最后一个位置向前开始循环
for (num = size -1; num >= 1; num--) {
for (num1 = 0; num1 <= num - 1; num1++) {
// 如果下标是num1的存储区里的数字比下表是(num)的大, 那两个存储区里的内容做交换
if (*(p_val + num1) > *(p_val + num)) {
tmp = *(p_val + num1);
*(p_val + num1) = *(p_val + num);
*(p_val + num) = tmp;
}
}
}
}
//插入排序函数
void insert_sort(int *p_val, int size) {
//最开始把下标为1的数字往下标为0的存储区里插
int num = 0, num1 = 0, tmp = 0;
for (num = 1; num <= size - 1; num++) {
//每次循环把下标为num的存储区里的数字向前插入, num1代表想插入的存储区的下标
//每次移动一步, 即先插到num-1的存储区, 再插到num-2的存储区...下标为0的存储区为止
for (num1 = num -1; num1 >= 0; num1--) {
//每次循环把选好的数字向下标为num1的存储区里插入
//这时, 选好(满足if)的数字一定在num1+1的存储区里
if (*(p_val + num1) > *(p_val + num1 + 1)) {
// 要插入位置的数字比选好的数字大就交换他们
tmp = *(p_val + num1);
*(p_val + num1) = *(p_val + num1 + 1);
*(p_val + num1 + 1) = tmp;
}
}
}
}
//快速排序函数
void quick_sort(int *p_val, int size) {
int base = *p_val; //把最前面的数字作为选定数字记录到base变量里, 用选定数字和别的没处理过的存储区的数字做顺序调整
int *p_head = p_val; //记录要处理的两个存储区中前面的那个, 最开始要指向数组最前面的存储区
int *p_tail = p_val + size -1; //记录要处理的两个存储区中后面的那个, 最开始的时候指向数组最后的存储区
int tmp = 0;
if (size <= 1) {
//数组里的存储区少于等于一个时候, 不需要再做任何排序处理
return ;
}
// p_head 向后移动, p_tail 向前移动, 只要它们还没相遇就说明还有存储区没有被处理, 就需要继续处理
while (p_head < p_tail) {
if (*p_head > *p_tail) {
//要处理的两个存储区中前面的存储区中的数大就交换它们的内容
tmp = *p_head;
*p_head = *p_tail;
*p_tail = tmp;
}
// 然后把上面两个处理过的存储区中不是选定数字的存储区(前后存储区都有可能), 给排除在外
if (*p_head == base) {
//如果前面的存储区的内容是选定数字, 就应该排除后面的存储区
p_tail--; //即把后面的存储区向前挪一步, 参考第1步中的不"再考虑70"
}
else {
//如果后面的存储区的内容是选定数字, 就应该排除前面的存储区
p_head++; // 参考第2步中的不再考虑20
}
}
// 当while结束以后, p_head 和 p_tail 都指向选定数字的存储区
// 下面对左半边数字进行排序
// 第一个参数:左半边存储区第一个存储区的地址, 即整个数组中第一个存储区的地址
// 第二个参数:左半边存储区的个数,即 (p_head or p_tail) - p_val , 两个地址相减应该是两个地址之间包含的存储区的个数
quick_sort(p_val, p_head - p_val);
// 下面对右边数字进行排序
// 第一个参数:右半边存储区第一个存储区的地址, 即 p_head + 1
// 第二个参数:右半边存储区的个数, 即 size - (p_head - p_val) -1
quick_sort(p_head + 1, size - (p_head - p_val) -1);
}
int main() {
int arr[] = {40, 80, 50, 20, 30};
int num = 0;
bubble_sort(arr, 5);
choice_sort(arr, 5);
insert_sort(arr, 5);
quick_sort(arr, 5);
for (num = 0; num <= 4; num++) {
printf("%d ", *(arr+num));
}
printf("\n");
}
//output
// 20 30 40 50 80
查找算法
- 1. 概念
- 从一组数字里找到某个数字所在的位置
- 2. 顺序查找算法
- 用每个数字和要查找的数字做对比直到找到为止
- 如果数字之间没有任何规律就只能用这种方法查找
- 3. 折半查找法(二分查找法)
- 要求所有数字已经按照某种顺序排列好了啦
- 每次用中间位置的数字和要查找的数字做对比,这样可以排出一半的数字. 重复这个过程就可以很快找到要查找的数字
#include <stdio.h>
//折半查找函数
int *half_search(const int *p_val, int size, int val) {
//在查找过程中需要随时知道查找范围和范围中间位置, 即 最前面和最后面和中间位置的存储区的地址
//函数里所有指针都应该有const关键字, 避免编译警告
const int *p_head = p_val; //指向要查找范围里最前面的存储区, 最开始的时候指向数组里最前面的存储区
const int *p_tail = p_val + size -1; //指向要查找范围里最后面的存储区, 最开始的时候指向数组里最后面的存储区
const int *p_mid = NULL; //指向要查找范围中间位置的存储区
//查找过程分为很多次进行,每次排除一半的存储区,于是要写一个循环
//找不到一组数字让循环变量来代表他们,所以不能用for
//在while和 和 do while 中选择 while , 因为在循环过程中有可能不需要做任何查找的(比如size为0时)
while (p_head <= p_tail) { //p_head向前移动, p_tail 向后移动, 只要没有遇到,就说明还有存储区需要处理
//每次循环排除一半的数字, (p_tail - p_head + 1) 是待查找范围里所有存储区的总个数
p_mid = p_head + (p_tail - p_head + 1) / 2;
//拿中间位置存储区内容和要找的数字做大小对比, 三种可能
if (*p_mid == val) {
//1. 中间位置存储区的内容就是要查找的数字
//强制类型转换避免编译警告, 因为返回值声明时没有const关键字,
return (int *)p_mid;
}
else if (*p_mid > val)
{
//2. 中间位置存储区的内容比要查找的数字大
//说明要查找的数字在左半边, 这时就可以把整个右半边存储区已经中间位置存储区全排除了
p_tail = p_mid - 1;
}
else
{
//中间位置存储区内容比要查找数字小
p_head = p_mid + 1; // 排除左半边存储区以及中间位置存储区
}
}
//有可能找不到要查找的数字, 循环正常结束
return NULL;
}
int main() {
int arr[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
int val = 0, *p_val = NULL;
printf("请输入要查找的数字: ");
scanf("%d", &val);
p_val = half_search(arr,10, val);
if (p_val) {
printf("找到数字是%d\n", *p_val);
}
else {
printf("没有找到数字\n");
}
}
//output
//请输入要查找的数字: 80
//找到数字是80
C++ 版本,可读性更高
void bub_sort(std::vector<int> *input)
{
assert(input != nullptr);
const int input_size = input->size();
for (int i = 0; i < input_size; i++)
{
for (int j = 0; j + 1 < input_size - i; j++)
{
if (input->at(j) > input->at(j + 1))
{
int tmp = input->at(j);
input->at(j) = input->at(j + 1);
input->at(j + 1) = tmp;
}
std::cout << i << std::endl;
for (const auto i : *input)
{
std::cout << i << ", ";
}
std::cout << std::endl;
}
}
}
void quick_sort(int left, int right, std::vector<int> *input) {
assert(input != nullptr);
if (left > right) return;
int i = left;
int j = right;
int tmp = input->at(left);
while (i != j) {
while (input->at(j) >= tmp && i < j) {
j--;
}
while (input->at(i) <= tmp && i < j) {
i++;
}
if (i < j) {
int t = input->at(i);
input->at(i) = input->at(j);
input->at(j) = t;
}
}
input->at(left) = input->at(i);
input->at(i) = tmp;
quick_sort( left, i-1, input);
quick_sort(i+1, right, input);
return;
}
int main()
{
std::vector<int> test_vec = {5, 4, 3, 2, 1};
// bub_sort(&test_vec);
quick_sort(0, test_vec.size() - 1, &test_vec);
for (const auto i : test_vec)
{
std::cout << i << ", ";
}
std::cout << std::endl;
}