8.排序算法

1.常用的算法:4个排序算法和2个查找算法

        1.1.冒泡排序算法

          例如:

                初始状态:9 7 5 3 1

                第一趟:7 5 3 1 9(4)

                第二趟:5 3 1 7 9(4)

                第三趟:3 1 5 7 9(4)

                第四趟:1 3 5 7 9(4)

        目前来看:N个数据需要N-1趟排序,每趟需要N-1次比较

        优化:

                初始状态:9 7 5 3 1

                第一趟:7 5 3 1 9(4)

                第二趟:5 3 1 7 9(3)

                第三趟:3 1 5 7 9(2)

                第四趟:1 3 5 7 9(1)

        目前来看:N个数据需要N-1趟排序,每趟需要N-i次比较

        优化:

                初始状态:0 7 5 3 1

                第一趟:0 5 3 1 7

                第二趟:0 3 1 5 7

                第三趟:0 1 3 5 7

                第四趟:0 1 3 5 7

        结论:如果某趟排序中,发现没有发生数据的交换,则认为排序完成 

        参考代码:sort目录

2.插入排序算法

例如:

        初始状态:10 30 20 15 5 //无序的

        原始插入排序思想:5 10 15 20 30 //有序的

        思路:从无序的数列中取出数据单独放到一个有序的数列中,存放时找到自己对应的位置即可

        弊端:需要额外开辟内存来存储有序数列

        原地实现插入排序流程:

        初始状态:10 30 20 15 5 //无序的

                             5 10 15 20 30

               ·             5//data变量暂存被插入的数据

        思路:定义一个变量先暂存被插入的数据,然后拿着被插入的数据在前面的有序数列中从后往前扫描 ,找到对应的位置插入即可 

3.选择排序算法

        例如:

                初始值:9 7 5 3 1 //无序的

                原始思想:先额外开辟一块内存存储有序数列,从无序的数列中取出最小的数放到有序数列中,然后从剩余无序的数列

                中再选择一个最小的放到有序的第二个,以此类推直到剩余一个

                弊端:需要额外开辟内存来存储有序数列

                解决办法原地完成选择排序:

                初始值:9 7 5 3 1

                                 -----------

                                1 7 5 3 9

                                 -----------

                                1 3 5 7 9

                                 -----------

                                1 3 5 7 9

                                 -----------

                                1 3 5 7 9

-

        思路:在无序数列中选择一个最小的放到无序的第一个,依次重复直到剩余最后一个

4.快速排序算法

        例如:0 10 80 30 60 50 40 70 20 90 100

        思路:不管站在哪个数上去看,它左边的数小于他,它右边的数大于它

        原始实现:比如站在50来看,进行一次分组,把比50小的放左边,大的放右边,得到:

        0 10 30 40 20 50 60 70 90 80 100

        然后对50左边的数列进行再次分组,站在30来看,比它小的放左边,大的放右边

        然后对50右边的数列进行再次分组,站在90来看,比它小的放左边,大的放右边,得到:

        0 20 10 30 40 50 60 80 70 90 100

        以此类推,对30左边和右边再次分组,直到剩余最后一个或者没有无需分组

        对90左边和右边再次分组,直到剩余最后一个或者没有无需分组

        等到数列实在无法再分组,数列必然有序

        0 10 80 30 60 50 40 70 20 90 100

        50基准: 0 10 30 40 20 50 60 70 90 80 100

        30,90基准: 0 20 10 30 40 50 60 80 70 90 100

        20,80基准: 10 0 20 30 40 50 70 60 80 90 100

        10,70基准: 0 10 20 30 40 50 60 70 80 90 100

        弊端:需要额外开辟内存来存储有序数列

        原地实现快排:定义三个游标(三个变量,数据的下标)来实现原地分组(p=pivot=基准),流程:

        以50为基准进行一次分组//pivot变量暂存基准值

        0 10 80 30 60 50 40 70 20 90 100

        i

        p

        j

        这里,i 指向左边界,j 指向右边界,p 指向基准值

        而后,i与基准值比较,0小于50,i++,当i到 80,比基准值大,故而将p指向的值变为80,而后将 p 指向 i 的位置,如下

        0 10 80 30 60 80 40 70 20 90 100

        i

        p

        j

        而后 j 从100 向左与基准值左比较,当指向20时,如下

        0 10 80 30 60 80 40 70 20 90 100

        i

        p

        j

        此时,将 j 的20挪到 p 的位置,将p挪到 j 的位置,而后右对 i 进行操作,20 <50, i++,直到指向60

        0 10 20 30 60 80 40 70 20 90 100

        i

        p

        j

        60比基准值大,顾把60挪到p的位置, 然后把p挪到 i 的位置,然后 对j进行操作,j--.直到指向40

        0 10 20 30 60 80 40 70 60 90 100

        i

        p

        j

        此时将 j 的 40 挪到 p 的位置,p 挪到 j ,而后对 i 进行操作,i++ 到80

        0 10 20 30 40 80 40 70 60 90 100

        i

        p

        j

        而后将80挪到p的位置, p挪到 i ,对 j操作 j--, 发现 i j位置重合,结束分组,此时将基准值50放到p位置

        0 10 20 30 40 50 80 70 60 90 100

        i

        p

        j

        vim sort.h

//sort.h:声明
#ifndef __SORT_H
#define __SORT_H
#include <stdio.h>
extern void bubble_sort(int data[], int size); //冒泡
extern void insert_sort(int data[], int size); //插入
extern void select_sort(int data[], int size); //选择
extern void quick_sort(int data[], int left, int right);//快排
#endif

        vim sort.c

//sort.c:定义
#include "sort.h"
//定义冒泡排序算法函数
void bubble_sort(int data[], int size) {
    int i, j;
    for(i = 0; i < size - 1; i++) { //排序的趟次
        int ordered = 1;//记录数据是否交换了,如果交换ordered=0,没有交换ordered=1
        for(j = 0; j < size-i-1; j++) {//每趟比较的次数
            if(data[j+1] < data[j]) {//交换位置
                int swap = data[j];
                data[j] = data[j+1];
                data[j+1] = swap;
                ordered = 0;//如果交换位置,ordered置0
            }
        }
        //每趟比较完成之后,判断是否发生了数据交换
        //如果交换了则继续后续趟次的比较
        //如果没有交换,则退出后续趟次比较,认为排序完成
        if(ordered)
            break;
    }
}
//定义插入排序算法
void insert_sort(int data[], int size) {
    int i;
    for(i = 1; i < size; i++) { //第一个数据本身有序,无需研究
        int inserted = data[i]; //暂存被插入的数据
        int j;
        //从后往前扫描找到对应的位置
        for(j = i; j > 0 && inserted < data[j-1]; --j)
            data[j] = data[j-1];//交换位置
        //如果被插入的数据比前面的数据都大,位置不动,无需额外将数据往自己的位置重新插入
        //10 20 30 40
        //40=inserted
        if(j != i)
            data[j] = inserted; //插入数据
    }
}
//定义选择排序算法
void select_sort(int data[] /*int *data*/, int size) {
    int i;
    for(i = 0; i < size - 1; i++) { //找最小数的趟次,最后一个数据本身有序
        int min = i; //假设无序数列中的第1个数据最小,min记录最小数的下标
        int j;
        for(j = i + 1; j < size; j++) //找最小数
            if(data[j] < data[min])
                min = j; //更新最小数的下标
        if(min != i) { //交换
            int swap = data[i];
            data[i] = data[min];
            data[min] = swap;
        }
    }
}
//快速排序
void quick_sort(int data[], int left, int right) {
    int p = (left+right)/2; //定义基准值,随意
    int i = left; //left=0;左边界
    int j = right; //right=size-1;右边界
    int pivot = data[p]; //暂存基准值
    while(i < j) { //进行一次分组,直到i=j停止分组
        //i不做i++运算的两种情况:i对应的值大于基准值或者i和p重合
        for(; !(i >= p || pivot < data[i]); i++);
        //此种情况是因为i对应的值大于基准值而退出上面的for循环,此时交换位置,
        //而不是i>=p这种情况
        if(i < p) {
            data[p] = data[i]; //将i对应的值挪到p的位置
            p = i;//调整p到i的位置
        }
        //j不做j--运算的两种情况:j对应的值小于基准值或者j和p重合
        for(;!(j <= p || data[j] < pivot); j--);
        //此种情况是因为j对应的值小于基准值而退出上面的for循环,此时交换位置
        if(j > p) {
            data[p] = data[j]; //将j的值挪到p的位置
            p = j;//调整p到j的位置
        }
    }
    data[p] = pivot; //分组结束将基准值放到p的位置
    if(p - left > 1)//如果基准值左侧的数据个数大于等于2个继续分组直到剩余1个或者没有
        quick_sort(data, left, p-1);//左边界还是0,右边界不包含基准值p
    if(right - p > 1)//如果基准值右侧的数据个数大于等于2个继续分组直到剩余1个或者没有
        quick_sort(data, p+1, right);//左边界不包含基准值p,右边界还是right
}

        vim main.c

//main.c:测试
#include "sort.h"
int main(void) {
    int data[] = {9, 0, 7, 2, 5, 4, 1, 6, 3, 8};
    int size = sizeof(data)/sizeof(data[0]);
    //bubble_sort(data, size); //冒泡
    //insert_sort(data, size); //插入
    //select_sort(data, size); //选择
    quick_sort(data, 0, size-1); //快排
    for(int i = 0; i < size; i++) 
        printf("%d ", data[i]);
    printf("\n");
    return 0;
}

        vim Makefile

#Makefile
BIN=sort
OBJ=main.o sort.o
CC=gcc

$(BIN):$(OBJ)
	$(CC) -o $(BIN) $(OBJ)

%.o:%.c
	$(CC) -c -o $@ $<

clean:
	rm $(BIN) $(OBJ)

2.查找算法

2.1.线性查找算法:

        在数列中挨个匹配查找想要的数据,对数据有序性无要求

2.2.二分查找(折半查找)

        注意:此方法使用的前提是数据必须有序,无序则先排序

        原理:10 20 30 40 50 60 70 //目标找60

      对数列对半分,拿着目标值和中间值进行比较,要不找到了要不继续左侧二分找要不继续右侧二分找

        vim find.h

//find.h:声明
#ifndef __FIND_H
#define __FIND_H
#include <stdio.h>
extern int line_find(int data[], int size, int key);//线性
extern int half_find(int data[], int size, int key);//二分
#endif

        vim find.c

//find.c:定义
#include "find.h"
//定义线性查找函数
int line_find(int data[], int size, int key) {
    for(int i = 0; i < size; i++)
        if(data[i] == key)
            return i; //找到并且返回下标
    return -1; //没有找到
}
//定义递归二分查找函数
static int recu_find(int data[], int left, int right, int key) {
    if(left <= right)  {
        int mid = (left+right)/2;
        if(key < data[mid])
            return recu_find(data, left, mid-1, key); //左边找
        else if(key > data[mid])
            return recu_find(data, mid+1, right, key); //右边找
        else
            return mid; //找到了
    }
    return -1; //没有找到
}
//定义二分查找
int half_find(int data[], int size, int key) {
    //调用递归函数来实现
    return recu_find(data, 0, size-1, key);
#if 0
    //不用递归
    int left = 0;
    int right = size-1;
    while(left <= right) {
        int mid = (left+right)/2; //二分
        if(key < data[mid]) //左侧继续找
            right = mid - 1;//重新定义右边界,中间值不要
        else if(key > data[mid]) //右侧继续找
            left = mid + 1; //重新定义左边界,中间值不要
        else
            return mid; //找到了
    }
    return -1; //没有找到
#endif
}

        vim main.c

//main.c:测试
#include "find.h"
#include "sort.h"
int main(void) {
    int data[] = {9, 0, 7, 2, 5, 4, 1, 6, 3, 8};
    int size = sizeof(data)/sizeof(data[0]);
    int key = 7;
    int index = -1;
    //线性
    index = line_find(data, size, key);
    if(index == -1) {
        printf("没有找到.\n");
        return -1;
    }
    printf("找到:data[%d] = %d\n", index, data[index]);
    //先快排
    quick_sort(data, 0, size-1);
    //后二分
    index = half_find(data, size, key);
    if(index == -1) {
        printf("没有找到.\n");
        return -1;
    }
    printf("找到:data[%d] = %d\n", index, data[index]);
    return 0;
}

        vim Makefile

#Makefile
BIN=find
OBJ=main.o find.o sort.o
CC=gcc

$(BIN):$(OBJ)
	$(CC) -o $(BIN) $(OBJ)

%.o:%.c
	$(CC) -c -o $@ $<

clean:
	rm $(BIN) $(OBJ)

算法执行的时间跟很多因素相关:

        1.跟代码的实现方式相关:函数/宏函数

        2.跟编程语言相关:C,python

        3.跟编译器相关:inline

        4.跟硬件相关:单核,多核

        5.操作系统相关:单任务/多任务

        6.平均时间复杂度:O

        空间复杂度:S

        冒泡/选择/插入:O(N^2)

        快排:O(NlogN)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值