010常见的排序和查找算法

1、排序算法的性能分析:

不同的排序算法性能不同,详细性能数据如下表所示。

从表中可以得到一些简单的指导思路:

  1. 选择排序、插入排序和冒泡排序思路简单,但时间效率较差,只适用于数据样本较小的场合,这几种算法的好处是不需要额外开辟空间,空间复杂度是常量。
  2. 希尔排序是插入排序的改进版,在平均情况下时间效率要比直接插入法好很多,也不需要额外开辟空间,要注意的是希尔排序是不稳定排序。
  3. 快速排序是所有排序算法中时间效率最高的,但由于快排是一种递归运算,对内存空间要求较高,当数据量较大时,会消耗较多的内存。

 2、排序算法的种类:

a、插入排序:假设前面已经有i节点是有序的,那么就从第i+1个节点开始,插入到前面的i个节点的合适的位置中。由于第一个元素自身总是有序的,因此从第2个开始,不断插入前面的有序序列,直到全部排列完毕。

假设总共有n个节点,那么总共需要将n−1个节点插入到有序序列中,而插入节点时需要找到合适的位置,显然这个查找的过程时间复杂度是O(n−i),因此插入排序的时间复杂度是O(n−1)(n−i),即O(n2)。

代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define DATA_SIZE       100     // 内存元素的个数
#define RAND_NUM_RANGE  1000    // 随机数的获取范围

/**
  * @brief  显示数据
  * @note   None
  * @param  data_p:指向要显示数据内存的指针
  *         len:   数据内存的长度
  * @retval None
  */
void INSERT_SORT_Show(int data_p[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d  ", *(data_p+i));
    }
    printf("\n");
}


/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  size: rand函数随机数范围的最大值
  * @retval 返回获取得到的随机数
  */
int INSERT_SORT_Rand(int size)
{
    // 1、判断传进来的范围值,是否超出范围
    if(size > 32767)
        return -1;

    // 2、返回随机数
    return rand()%(size+1);
}


/**
  * @brief  选择排序
  * @note   None
  * @param  data_p:指向要排序的内存的指针
  *         len:   排序内存元素的个数
  * @retval None
  */
void INSERT_SORT_DataSort(int data_p[], int len)
{
    // 小于等于1,就没有必要进行排序了
    if(len <= 1)
        return;

    int tmp_data = 0;
    int i = 0;
    int j = 0;

    for (i = 1; i < len; i++)
    {
        // 每次循环开始要比较的值
        tmp_data = data_p[i];

        // 一次循环的规律
        for (j = i-1; j >= 0; j--)
        {
            if (data_p[j] < tmp_data)
            {
                break;
            }
            else
            {    
                data_p[j+1] = data_p[j];    // 移动数据(向右边移动数据)
            } 
        }
        data_p[j+1] = tmp_data;             // 将比较的数据,插入到比这此循环数据小的前面一个位置
    }
}


// 主函数
int main(int argc, char const *argv[])
{
    // 0、我们是伪随机,所以需要有一个随时变化的种子(时间)
    srand(time(NULL));              

    // 1、随机生成数据
    int data_buf[DATA_SIZE] = {0};
    for (int i = 0; i < DATA_SIZE; i++)
    {
        data_buf[i] =  INSERT_SORT_Rand(RAND_NUM_RANGE);
    }
    
    // 2、打印生成的随机数据(原数据)
    printf("原数据:\n");
    INSERT_SORT_Show(data_buf, DATA_SIZE);

    printf("排序后的数据:\n");
    INSERT_SORT_DataSort(data_buf, DATA_SIZE);
    INSERT_SORT_Show(data_buf, DATA_SIZE);
    
    return 0;
}

b、冒泡排序

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define DATA_SIZE       100     // 内存元素的个数
#define RAND_NUM_RANGE  1000    // 随机数的获取范围

/**
  * @brief  交换两个数据
  * @note   默认整型数据(int、char等)
  * @param  num1:要交换数据1
  *         num2:要交换数据2
  * @retval None
  */
void BUBBLE_SORT_Swap(int *num1, int *num2)
{
    int tmp;        // 交换两个数据的中间值
    
    tmp   = *num1;  // 想一想装满可乐的瓶子想要倒进装满七喜的瓶子,应该怎么做? 先拥有一个空瓶子 
    *num1 = *num2;
    *num2 = tmp;
}

/**
  * @brief  显示数据
  * @note   None
  * @param  data_p:指向要显示数据内存的指针
  *         len:   数据内存的长度
  * @retval None
  */
void BUBBLE_SORT_Show(int data_p[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d\t", *(data_p+i));
    }
    printf("\n");
}


/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  size: rand函数随机数范围的最大值
  * @retval 返回获取得到的随机数
  */
int BUBBLE_SORT_Rand(int size)
{
    // 1、判断传进来的范围值,是否超出范围
    if(size > 32767)
        return -1;

    // 2、返回随机数
    return rand()%(size+1);
}


/**
  * @brief  冒泡排序
  * @note   None
  * @param  data_p:指向要排序的内存的指针
  *         len:   排序内存元素的个数
  * @retval 成功:返回0
  *         失败:返回非0
  */
int BUBBLE_SORT_DataSort(int data_p[], int len)
{
    // (1)、判断传进来的指向需要排序的内存的指针,是否为NULL,为空退出
    if (len <= 1)
    {
        return -1;
    }

    // (2)、
    for (int i = 0; i < len-1; i++)             // 让其能够遍历所有可能
    {
        for (int j = 0; j < len-i-1; j++)       // 多次遍历,将排序号的数据,移动到最末尾
        {
            if (data_p[j] > data_p[j+1]) 
            {
                // 交换
                BUBBLE_SORT_Swap(&data_p[j], &data_p[j+1]);
            }
        }
    }   
    
    // (3)、成功返回0
    return 0;
}

// 主函数
int main(int argc, char const *argv[])
{
    // 0、我们是伪随机,所以需要有一个随时变化的种子(时间)
    srand(time(NULL));              

    // 1、随机生成数据
    int data_buf[DATA_SIZE] = {0};
    for (int i = 0; i < DATA_SIZE; i++)
    {
        data_buf[i] =  BUBBLE_SORT_Rand(RAND_NUM_RANGE);
    }
    
    // 2、打印生成的随机数据(原数据)
    printf("原数据:\n");
    BUBBLE_SORT_Show(data_buf, DATA_SIZE);

    printf("排序后的数据:\n");
    BUBBLE_SORT_DataSort(data_buf, DATA_SIZE);
    BUBBLE_SORT_Show(data_buf, DATA_SIZE);
    
    return 0;
}

c、选择排序

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define DATA_SIZE       100     // 内存元素的个数
#define RAND_NUM_RANGE  1000    // 随机数的获取范围

/**
  * @brief  交换两个数据
  * @note   默认整型数据(int、char等)
  * @param  num1:要交换数据1
  *         num2:要交换数据2
  * @retval None
  */
void SELECT_SORT_Swap(int *num1, int *num2)
{
    int tmp;        // 交换两个数据的中间值
    
    tmp   = *num1;  // 想一想装满可乐的瓶子想要倒进装满七喜的瓶子,应该怎么做? 先拥有一个空瓶子 
    *num1 = *num2;
    *num2 = tmp;
}

/**
  * @brief  显示数据
  * @note   None
  * @param  data_p:指向要显示数据内存的指针
  *         len:   数据内存的长度
  * @retval None
  */
void SELECT_SORT_Show(int data_p[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d  ", *(data_p+i));
    }
    printf("\n");
}


/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  size: rand函数随机数范围的最大值
  * @retval 返回获取得到的随机数
  */
int SELECT_SORT_Rand(int size)
{
    // 1、判断传进来的范围值,是否超出范围
    if(size > 32767)
        return -1;

    // 2、返回随机数
    return rand()%(size+1);
}


/**
  * @brief  选择排序
  * @note   None
  * @param  data_p:指向要排序的内存的指针
  *         len:   排序内存元素的个数
  * @retval None
  */
void SELECT_SORT_DataSort(int data_p[], int len)
{
    // 如果比较数据小于等于1,那么没有必要进行比较
    if(len <= 1)
        return;

    int min_num = 0;
    for (int i = 0; i < len; i++)
    {
        // 下一次循环的标记点(从这个数开始比较)
        min_num = i;        

        // 一次循环的规律
        for (int j = i; j < len; j++)
        {
            if(data_p[j] < data_p[min_num])     // 比这一轮的标记的位置的元素小,重新标记元素的位置
                min_num = j;            
        }
        // 交换两个数据(数据1:此次循环的第一个数据; 数据2:此次循环的标记的那个数据)
        SELECT_SORT_Swap(&data_p[i], &data_p[min_num]);
    }
}


// 主函数
int main(int argc, char const *argv[])
{
    // 0、我们是伪随机,所以需要有一个随时变化的种子(时间)
    srand(time(NULL));              

    // 1、随机生成数据
    int data_buf[DATA_SIZE] = {0};
    for (int i = 0; i < DATA_SIZE; i++)
    {
        data_buf[i] =  SELECT_SORT_Rand(RAND_NUM_RANGE);
    }
    
    // 2、打印生成的随机数据(原数据)
    printf("原数据:\n");
    SELECT_SORT_Show(data_buf, DATA_SIZE);

    printf("排序后的数据:\n");
    SELECT_SORT_DataSort(data_buf, DATA_SIZE);
    SELECT_SORT_Show(data_buf, DATA_SIZE);
    
    return 0;
}

d、快速排序

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define DATA_SIZE       100     // 内存元素的个数
#define RAND_NUM_RANGE  1000    // 随机数的获取范围
int count = 0;

/**
  * @brief  交换两个数据
  * @note   默认整型数据(int、char等)
  * @param  num1:要交换数据1
  *         num2:要交换数据2
  * @retval None
  */
void QUICK_SORT_Swap(int *num1, int *num2)
{
    int tmp;        // 交换两个数据的中间值
    
    tmp   = *num1;  // 想一想装满可乐的瓶子想要倒进装满七喜的瓶子,应该怎么做? 先拥有一个空瓶子 
    *num1 = *num2;
    *num2 = tmp;
}


/**
  * @brief  显示数据
  * @note   None
  * @param  data_p:指向要显示数据内存的指针
  *         len:   数据内存的长度
  * @retval None
  */
void QUICK_SORT_Show(int data_p[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d  ", *(data_p+i));
    }
    printf("\n");
}


/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  size: rand函数随机数范围的最大值
  * @retval 返回获取得到的随机数
  */
int QUICK_SORT_Rand(int size)
{
    // 1、判断传进来的范围值,是否超出范围
    if(size > 32767)
        return -1;

    // 2、返回随机数
    return rand()%(size+1);
}



/**
  * @brief  快速排序
  * @note   None
  * @param  data_p:指向要排序的内存的指针
  *         len:   排序内存元素的个数
  * @retval None
  */
int QUICK_SORT_DataSort(int data_p[], int len)
{
    // 1、退出条件
    if(len <= 1)
        return 0;

    int i = 0;
    int j = len-1;      // 2、不断递进的过程

    // 这里是为了找到支点,以方便传给下一次递归
    while (i<j) 
    { 
        // 从右向左比较,顺序j--,逆序交换
        while( (data_p[i] <= data_p[j]) && (i<j) )  
        {
            j--;                                
        }
        QUICK_SORT_Swap(&data_p[i], &data_p[j]); 

        // 从左向右比较,顺序i++,逆序交换
        while( (data_p[i] <= data_p[j]) && (i<j))   
        {
            count++;
            i++;                                
        }
        QUICK_SORT_Swap(&data_p[i], &data_p[j]);  
    }

    // 3、不断逼进结果
    int num = QUICK_SORT_DataSort(data_p, i);           // 解决左边序                                        
    QUICK_SORT_DataSort(data_p + num+1, len-num-1);     // 解决右边序

}



// 主函数
int main(int argc, char const *argv[])
{
    // 0、我们是伪随机,所以需要有一个随时变化的种子(时间)
    srand(time(NULL));              

    // 1、随机生成数据
    int data_buf[DATA_SIZE] = {0};
    for (int i = 0; i < DATA_SIZE; i++)
    {
        data_buf[i] =  QUICK_SORT_Rand(RAND_NUM_RANGE);
    }
    
    // 2、打印生成的随机数据(原数据)
    printf("原数据:\n");
    QUICK_SORT_Show(data_buf, DATA_SIZE);

    printf("排序后的数据:\n");
    QUICK_SORT_DataSort(data_buf, DATA_SIZE);
    printf("count == %d\n", count);
    QUICK_SORT_Show(data_buf, DATA_SIZE);
    
    return 0;
}

e、希尔排序

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define DATA_SIZE       100     // 内存元素的个数
#define RAND_NUM_RANGE  1000    // 随机数的获取范围

int count = 0;
/**
  * @brief  显示数据
  * @note   None
  * @param  data_p:指向要显示数据内存的指针
  *         len:   数据内存的长度
  * @retval None
  */
void HILL_SORT_Show(int data_p[], int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%d  ", *(data_p+i));
    }
    printf("\n");
}


/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  size: rand函数随机数范围的最大值
  * @retval 返回获取得到的随机数
  */
int HILL_SORT_Rand(int size)
{
    // 1、判断传进来的范围值,是否超出范围
    if(size > 32767)
        return -1;

    // 2、返回随机数
    return rand()%(size+1);
}


/**
  * @brief  希尔排序
  * @note   None
  * @param  data:  指向要排序的内存的指针
  *         len:   排序内存元素的个数
  *         delta: 间隔排序数
  * @retval None
  */
void HILL_SORT_DataSort(int data[], int len, int delta)
{
    if(len <= 1)
        return;

    for(int i=delta; i<len*delta; i+=delta)
    {
        int j, tmp = data[i];
        for(j=i-delta; j>=0; j-=delta)
        {
            if(data[j] < tmp)
                break;
            data[j+delta] = data[j];
            count++;
            
        }

        data[j+delta] = tmp;
    }
}

/**
  * @brief  希尔排序(真)
  * @note   保持一定间隔来排序,不能的话,逐渐缩小间隔
  * @param  data:  指向要排序的内存的指针
  *         len:   排序内存元素的个数
  * @retval None
  */
void HILL_SORT_DataTrueSort(int data[], int len)
{
    if(len <= 1)
        return;

    // 将间隔一个个缩小(买彩票2000/1, 200/1, 20/1, 2/1, 1)
    for(int delta=len/2; delta>0; delta/=2)
    {
        for(int i=0; i<delta; ++i)
        {
            //                 起点     节点个数    间距
            HILL_SORT_DataSort(data+i, len/delta, delta);

        }
    }
}

// 主函数
int main(int argc, char const *argv[])
{
    // 0、我们是伪随机,所以需要有一个随时变化的种子(时间)
    srand(time(NULL));              

    // 1、随机生成数据
    int data_buf[DATA_SIZE] = {0};
    for (int i = 0; i < DATA_SIZE; i++)
    {
        data_buf[i] =  HILL_SORT_Rand(RAND_NUM_RANGE);
    }
    
    // 2、打印生成的随机数据(原数据)
    printf("原数据:\n");
    HILL_SORT_Show(data_buf, DATA_SIZE);

    printf("排序后的数据:\n");
    HILL_SORT_DataTrueSort(data_buf, DATA_SIZE);
    printf("count == %d\n", count);
    HILL_SORT_Show(data_buf, DATA_SIZE);
    
    return 0;
}

3、基本查找算法:

a、顺序查找:

顺序查找顾名思义,就是挨个找,这是一种不是算法的“算法”,最没办法的“办法”,简单直接,童叟无欺。时间复杂度就是 O(n)

适用情况:

  • 无序的数据
  • 无法(或不便于)对这些数据进行有效的整理

代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <strings.h>


#define DATA_NUM       10*1000     // 内存元素的个数
static  int count;                  // 计数找了多久


/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  range: 控制获取的随机数,至少是几位 
  * @retval 返回获取得到的随机数
  */
int SEQ_SEARCH_Rand(int range)
{
    return rand()%((int)pow(10, rand()%6+range+1));    // 获取随机数,无序
}

/**
  * @brief  查找数据
  * @note   None
  * @param  data_p: 指向要查找的数据的内存的指针
  *         len:   内存元素的个数
  *         num:   要查找的数据
  * @retval 失败:返回-1:内存没有数据存储, 返回0:内存没有找到这个数据
  *         成功: 返回获取得到的查找的数据
  */
int SEQ_SEARCH_DataGet(int data_p[], int len, int num)
{
    // 1、判断传进来的内存是否有数据
    if (len <= 0)
       return -1;

    // 2、查找数据
    for (int i = 0; i < len; i++)
    {
        // 计数找了右多久
        count++;

        // 判断是否是找到的的数据
        if(data_p[i] == num)
        {
            printf("你要找的数据%d在第%d行", data_p[i], i+1);
            return data_p[i];
        }
            
    }

    // 3、找不到
    return 0;
    
}

/**
  * @brief  将数据存储到txt文件中(数据太大,所以放文本里面,充当显示)
  * @note   None
  * @param  data:要写入数据的内存
  * @retval None
  */
void SEQ_SEARCH_StoreData(unsigned int *data, const char *file)
{
    FILE *fp = fopen(file, "w");                    // 1、创建并打开文件(有写权限)
    for(int i=0; i<DATA_NUM; i++)                   // 2、将海量数据写入到文本中 
        fprintf(fp, "%06d = %u\n", i, data[i]);     // 数据规模:100万(6位数)
    fclose(fp);                                     // 3、关闭文件
}


int main(int argc, char const *argv[])
{
    // (1)、产生无序数据,并赋值给相应的内存空间
    // 1、使用时间作为随机数的变化量
    srand(time(NULL));  


    // 2、申请堆空间,用来存放大数据  ---- PS: 栈空间:8M, 堆空间:几个G(看你的电脑) 如果堆空间都不够放,放硬盘(文件)
    unsigned int *data_p = malloc(sizeof(unsigned int)* DATA_NUM );
    bzero(data_p, sizeof(unsigned int)* DATA_NUM );


    // 3、存放数据
    for (int i = 0; i < DATA_NUM; i++)
    {
        *(data_p+i) = SEQ_SEARCH_Rand(1);
    }


    // (2)、将数据加载.txt文本中
    SEQ_SEARCH_StoreData(data_p, "./顺序查找文本.txt");     // 这里只做显示使用(通常来讲,是读取文件数据到堆空间(栈空间)里面再进行处理数据的)


    // (3)、查找数据
    int n = 0;

    while(1)
    {
        // 1、输入要找的数据
        printf("请输入你要找的正整数:\n");
        scanf("%u", &n);
        while(getchar()!='\n');

        // 2、放回找到的数据
        int ret = SEQ_SEARCH_DataGet(data_p, DATA_NUM, n);

        if(ret == -1)
            printf("data_p指向的内存的数据为空的!\n");
        else if(ret == 0)
            printf("没有在data_p指向的内存中找到此数据!\n");

        printf("[一共找了%d次]\n", count);
        count = 0;
    }

    
    return 0;
}

b、分块查找:

分块查找实质就是给数据建立索引,将查找的过程分成两个步骤:

  1. 在索引(目录)中找到数据所属分块
  2. 在所属分块中查找到该数据

以下例子中,先是产生系列长度和内容都随机的字符串,作为待查数据集。然后对这些字符串按首字符进行整理,形成字母表索引(目录)。最后利用索引提高查找效率。

核心代码示例:

#include <math.h>
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <strings.h>

// 数据规模: 100万
#define SCALE  1000*1000*2 // 字符串总数
#define MAXLEN 20        // 字符串最大长度

// 查找次数统计
static int count;

/**
  * @brief  生成随机字符数组(1)
  * @note   None
  * @param  None
  * @retval 成功:返回随机字符数组
            失败:None
  */
char *random_string(void)
{
    int len = rand()%(MAXLEN);          // 规定字符串的最长的长度为19
    len = ((len<2)? 2 : len);           // 规定字符串的最短的长度为2

    char *s = calloc(1, len);           // 申请len个字节的堆空间 
    char letter[] = {'a', 'A'};         // 随机你的字母为小写a还是大写A
                  
    for(int i=0; i<len-1; i++)          // 比如len为:19,那么i的范围为0~19-1
        s[i] = letter[rand()%2]+ rand()%26;
                                        // 随机你的字母为小写a还是大写A, 再在此基础上加0到25的数据,从而形成a到z,A到Z的随机字母
    return s;                           // 返回生成的随机字符数组
}


/**
  * @brief   将数据存储到txt文件中(2)
  * @note   None
  * @param  data:二维数组(里面的每一个一维数组,都是字符数组)
  * @retval None
  */
  
void store(char data[][MAXLEN], const char *file)
{
    FILE *fp = fopen(file, "w");               // 1、打开文件
                                               
    for(int i=0; i<SCALE; i++)                 // 2、将海量的数据,存储到文件(file(数据库))中
        fprintf(fp, "%06d = %s\n", i, data[i]);  // 数据规模:100万(6位数)格式:000001.ewgtyyewtgfw
                                               
    fclose(fp);                                // 3、关闭文件        
}

/**
  * @brief  在索引(目录)中找到数据所属分块(3)
  * @note   根据字符串开头字符的大小写来寻找
  * @param  data: 二维数组(里面的每一个一维数组,都是字符数组)
            index:二级指针(里面的每一个一级数组,都指向了一个字符数组)
  * @retval None
  */
void create_index(char data[][MAXLEN], int **index)
{
    // 1、统计各个首字符出现的频次
    int n[52]={0};                      // ['a', 'b', ... 'z', 'A', 'B', ... 'Z']
    for (int k = 0; k < SCALE; k++)     // 在100万行数据中找每一行字符串的首字符
    {
        // 小写字母[00~25],大写字母[26~51]
        int pos = ((data[k][0] >= 'a') ? (data[k][0]-'a') : (data[k][0]-'A'+26));   // 找到的首字符的ASCII值,其值大于'a',则为小写(算出其位置),其值小于'a',则为大写(算出其位置)
        n[pos]++;                       // 统计各个首字符出现的频次
    }

    // 2、给index分配内存
    // 每个字母分配一段存储以该字母为首的字符串所在的行号的内存
    // 第一个位置存储总行数,因此所需分配的内存单元数是1+n[i]。
    // 例如:
    // index[2] --> [ 242     3     22    213 ... ... 42513 46698]
    //   'c'    -->  总行数  第3行 第22行     ... ...
    
    for(int i=0; i<52; i++)
        index[i] = calloc(1+n[i], sizeof(int)); // a == n[0]  频次:100 


    // 3、记录每个字母出现的行号
    for(int i=0; i<SCALE; i++)
    {
        int pos = ((data[i][0] >= 'a') ? (data[i][0]-'a') : (data[i][0]-'A'+26));

        int k = ++index[pos][0];
        index[pos][k] = i;
    }
}

// 主函数
int main(int argc, char const *argv[])
{
    // 1. 产生随机字符串数据集
    //    假设每个字符串长度不超过MAXLEN个字符
    char (*data)[MAXLEN] = calloc(SCALE, MAXLEN);

    srand(time(NULL));
    for(int i=0; i<SCALE; i++)
    {
        char *s = random_string();
        strncpy(data[i], s, strlen(s));
        free(s);
    }
    store(data, "./分块查找文本.txt");

    // 2. 按首字母建立索引(分块)
    int **index = calloc(52, sizeof(int *));
    create_index(data, index);

    // 3. 利用索引,进行查找
    char str[32];
    printf("请输入你要查找的字符串:\n");
    while(1)
    {
        // 从键盘接收一个待查找的字符串并去掉回车符
        bzero(str, 32);
        fgets(str, 32, stdin);
        strtok(str, "\n");

        bool done = false;
        for(int i=1; i<SCALE; i++)
        {
            // 小写字母[00~25],大写字母[26~51]
            int pos = ((str[0]>='a') ? (str[0]-'a') : (str[0]-'A'+26));

            count++;
            if(i<=index[pos][0] && strcmp(data[index[pos][i]], str) == 0)
            {
                printf("你要找的字符串在第%d行", index[pos][i]);
                done = true;
                break;
            }
            else if(i > index[pos][0])
                break;
        }

        if(!done)
            printf("没有你要的字符串");

        printf("【找了%d次】\n", count);
        count=0;
    }

    return 0;
}

c、二分查找:

分块查找是在不对数据进行排序的情况下采用的颇为有效的查找办法,但如果待查找的数据本身是有序的,或者在查找前,可以对数据先进行排序(比如数据量虽然较大,但短期较稳定,无大面积更新),这种情况下使用二分查找可以进一步提升效率。

二分法的思路相当朴实无华:从中间开始找。既然数据是有序的,那么如果将待查找的节点跟中间节点对比,就可以以排除掉一半的数据,接着再在剩余的数据的中间开始找,又可以很快排除掉剩下的一半的数据,这种一半一半筛查数据的办法,就是所谓的二分法。

代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <strings.h>
#include <stdbool.h>

#define DATA_NUM       10*1000     // 内存元素的个数
static  int count;                  // 计数找了多久

/**
  * @brief  获取随机数,并返回
  * @note   None
  * @param  range: 控制获取的随机数,至少是几位 
  * @retval 返回获取得到的随机数
  */
int BIN_SEARCH_Rand(int range)
{
    return rand()%((int)pow(10, rand()%8+range+1));    // 获取随机数,无序
}

/**
  * @brief  查找数据
  * @note   二分查找法
  * @param  data_p: 要查找的数据内存
  *         len:   数据内存的长度
  *         num:    要查找的数据   
  * @retval 返回查找的数据的位置在第几行
  */
int BIN_SEARCH_DataGet(int data_p[], int len, int num)
{
    // 1、判断传进来的内存是否有数据
    if (len <= 0)
        return -1;

    // 2、查找数据
    int low     = 0; 
    int high    = len-1;
    int mid_val = 0;

    while(low <= high)
    {
        count++;

        mid_val = (low+high)/2;
        if(num == data_p[mid_val])
        {
            printf("你要找的数据在第%d行", mid_val);
            return data_p[mid_val];
            break;
        }

        if(num < data_p[mid_val])
            high = mid_val - 1;
        else
            low  = mid_val + 1;
    } 


    // 3、找不到数据
    return 0;

}

/**
  * @brief  将数据存储到txt文件中(数据太大,所以放文本里面,充当显示)
  * @note   None
  * @param  data:要写入数据的内存
  * @retval None
  */
void BIN_SEARCH_StoreData(unsigned int *data, const char *file)
{
    FILE *fp = fopen(file, "w");                    // 1、创建并打开文件(有写权限)
    for(int i=0; i<DATA_NUM; i++)                   // 2、将海量数据写入到文本中 
        fprintf(fp, "%06d = %u\n", i, data[i]);     // 数据规模:100万(6位数)
    fclose(fp);                                     // 3、关闭文件
}


/**
  * @brief  交换两个数据
  * @note   默认整型数据(int、char等)
  * @param  num1:要交换数据1
  *         num2:要交换数据2
  * @retval None
  */
void QUICK_SORT_Swap(int *num1, int *num2)
{
    int tmp;        // 交换两个数据的中间值
    
    tmp   = *num1;  // 想一想装满可乐的瓶子想要倒进装满七喜的瓶子,应该怎么做? 先拥有一个空瓶子 
    *num1 = *num2;
    *num2 = tmp;
}
/**
  * @brief  快速排序
  * @note   None
  * @param  data_p:指向要排序的内存的指针
  *         len:   排序内存元素的个数
  * @retval None
  */
int QUICK_SORT_DataSort(int data_p[], int len)
{
    // 1、退出条件
    if(len <= 1)
        return 0;

    int i = 0;
    int j = len-1;      // 2、不断递进的过程

    // 这里是为了找到支点,以方便传给下一次递归
    while (i<j)
    {
        // 从右向左比较,顺序j--,逆序交换
        while( (data_p[i] <= data_p[j]) && (i<j) )  
        {
            j--;                                
        }
        QUICK_SORT_Swap(&data_p[i], &data_p[j]); 

        // 从左向右比较,顺序i++,逆序交换
        while( (data_p[i] <= data_p[j]) && (i<j))   
        {
            i++;                                
        }
        QUICK_SORT_Swap(&data_p[i], &data_p[j]);  
     }

    // 3、不断逼进结果
    int num = QUICK_SORT_DataSort(data_p, i);           // 解决左边序                                        
    QUICK_SORT_DataSort(data_p + num+1, len-num-1);     // 解决右边序

}


// 主函数
int main(int argc, char const *argv[])
{
    // (1)、产生无序数据,并赋值给相应的内存空间
    // 1、使用时间作为随机数的变化量
    srand(time(NULL));  


    // 2、申请堆空间,用来存放大数据  ---- PS: 栈空间:8M, 堆空间:几个G(看你的电脑) 如果堆空间都不够放,放硬盘(文件)
    unsigned int *data_p = malloc(sizeof(unsigned int)* DATA_NUM );
    bzero(data_p, sizeof(unsigned int)* DATA_NUM );


    // 3、存放数据
    for (int i = 0; i < DATA_NUM; i++)
    {
        *(data_p+i) = BIN_SEARCH_Rand(1);
    }

    // (2)、将数据加载.txt文本中(未排序前的)
    //BIN_SEARCH_StoreData(data_p, "./二分查找文本_未排序.txt"); 

    // (3)、将数据排序(为了方便二分查找法查找数据)
    QUICK_SORT_DataSort(data_p, DATA_NUM);

    // (4)、将数据加载.txt文本中(已排序后的)
    BIN_SEARCH_StoreData(data_p, "./二分查找文本.txt");     // 这里只做显示使用(通常来讲,是读取文件数据到堆空间(栈空间)里面再进行处理数据的)

    // (3)、查找数据
    int n = 0;
    while(1)
    {
        // 1、输入要找的数据
        printf("请输入你要找的正整数:\n");
        scanf("%u", &n);
        while(getchar()!='\n');

        // 2、返回找到的数据
        int ret = BIN_SEARCH_DataGet(data_p, DATA_NUM, n);

        if(ret == -1)
            printf("data_p指向的内存的数据为空的!\n");
        else if(ret == 0)
            printf("没有在data_p指向的内存中找到此数据!\n");

        printf("[一共找了%d次]\n", count+1);
        count = 0;
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值