常用函数之排序专栏(重点qsort)

1.常用排序法

为了实现对一个数组的排序,有许多排序方法,其中比较著名的当属冒泡排序法、插入排序法、选择排序法。

(1)冒泡排序法

冒泡排序法是一种简单直观的排序算法,它重复地遍历要排序的数组,依次比较相邻的两个元素,如果它们的顺序不正确则交换它们的位置。通过多次遍历,将数组中最大的元素依次“冒泡”到数组的最后位置。冒泡排序的时间复杂度为O(n^2)。

#include <stdio.h>

void bubbleSort(int arr[], int n) 
{
    for (int i = 0; i < n-1; i++) 
    {
        int flag=0;
        for (int j = 0; j < n-i-1; j++) 
        {
            if (arr[j] > arr[j+1]) 
            {
                // 交换arr[j]和arr[j+1]的位置
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag=1;
            }
        }
        if(flag==0)
        {
            break;
        }
    }
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    bubbleSort(arr, n);//将数组中的元素从小到大进行排序

    printf("排序后的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

例题:

【问题描述】
平面上两个点(一个点由(x,y)坐标组成)可构成一个线段,两个线段如果有一个端点相同,则可构成一个连续线段。假设构成线段的两个端点为v1(x1,y1)和v2(x2,y2),在此满足x1<x2,其中v1称为线段的起点,v2为线段的终点。同样,对于连续线段来说,在此满足xi<xi+1(i=1...n-1,n为连续线段中的端点数,xi为相应端点的X轴坐标)。输入一组线段(用两个端点的x、y坐标表示线段,线段个数大于等于2,小于等于100),编程计算出连续线段中包含最多线段数的线段,输出相应的线段数和起点位置(注意,不是最长的连续线段,是包含最多线段的连续线段)。例如:

1.jpg

上图中有10个线段,其中5、10、4号线段连成了一条连续线段,线段数3条,起点位置为5号线段的左端点;6、8、2、3、9号线段连成了另一条连续线段,该连续线段包含的线段数最多,为5条,起点位置是6号线段的左端点。
注意:
1)不考虑线段中间相交的情况;
2)不会有三条或三条以上的线段共一个端点;
3)只会出现一条包含最多线段的连续线段;

【输入形式】
先从控制台输入线段数,然后从下一行开始分行输入各线段两个端点的x、y坐标,其中第一个端点的X轴坐标小于第二个端点的X轴坐标,即x1<x2,x、y坐标都用整数表示,不会超过int的表示范围。各整数坐标值间以一个空格分隔。
【输出形式】
先在控制台输出包含最多线段数的连续线段的线段数,然后输出连续线段的起点的x、y坐标,输出数据都以一个空格分隔
【样例输入】

10

80 75 125 75

60 40 80 55

80 55 90 20

140 120 195 205

10 111 70 165

22 35 43 43

22 175 80 205

43 43 60 40

90 20 125 60

70 165 140 120

【样例输出】
5 22 35
【样例说明】
输入了十个线段,第一个线段两个端点分别为(80,75)和(125,75),其它线段类似,如上图所示,这些线段所构成的连续线段中包含最多线段数的连续线段的线段数为5,起点为(22,35),所以输出:5 22 35。
【评分标准】
通过所有测试点将得满分。提交程序名为line.c。

#include <stdio.h>
struct point
{
    int a[2];
    int b[2];
}point[200],temp;
int arr[200];
int main()
{
    int n,i,j,l,k,max=0,c,d,flag=0;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d%d%d%d",&point[i].a[0],&point[i].a[1],&point[i].b[0],&point[i].b[1]);//读入端点信息
    for(i=0;i<n;i++)
    {
        flag=0;
        for(j=i+1;j<n;j++)
        {
            if(point[j].a[0]<point[i].a[0])
            {
                temp=point[j];
                point[j]=point[i];
                point[i]=temp;
                flag=1;
            }
        }
        if(flag==0)
            break;
    }//将线段按照前端点的大小从左到右排列起来
    for(i=0;i<n;i++)//每一次遍历都相当于找到以这条线段为起点的最长的线段
    {
        l=1;//l代表线段长度
        k=i;//k代表当前线段位置
        for(j=i+1;j<n;j++)
        {
            if(point[j].a[0]==point[k].b[0]&&point[j].a[1]==point[k].b[1])
            {
                l++;
                k=j;
            }
        }//从左到右遍历线段,观察是否有重合的端点
        if(l>max)
        {
            max=l;
            c=point[i].a[0];
            d=point[i].a[1];
        }//如果这条线段更长就更新
    }
    printf("%d %d %d",max,c,d);//输出数据
}

 

(2)插入排序法

插入排序法是一种通过构建有序序列,不断地将未排序元素插入到已排序部分的排序算法。它从第二个元素开始,在每一轮中将当前元素插入到已排序部分的适当位置。插入排序的时间复杂度取决于输入数据的初始顺序,平均情况下为O(n^2)。

#include <stdio.h>

void insertionSort(int arr[], int n) {
    int i, j, key;
    for (i = 1; i < n; i++) {
        key = arr[i];
        j = i - 1;
        
        // 将比 key 大的元素右移
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
}

int main() {
    int arr[] = {12, 11, 13, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    insertionSort(arr, n);

    printf("排序后的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}
(3)选择排序法

选择排序法是一种简单直观的排序算法,它的基本思想是每次从未排序部分选择最小(或最大)的元素,并将其与未排序部分的第一个元素进行交换,从而逐步构建有序序列。选择排序的主要特点是不稳定性和不适合大规模数据集,因为其时间复杂度为O(n^2)。

#include <stdio.h>

void selectionSort(int arr[], int n) {
    int i, j, min_idx;
    for (i = 0; i < n-1; i++) {
        min_idx = i;
        for (j = i+1; j < n; j++) {
            if (arr[j] < arr[min_idx]) {
                min_idx = j;
            }
        }
        // 将找到的最小元素与第 i 个元素交换
        int temp = arr[i];
        arr[i] = arr[min_idx];
        arr[min_idx] = temp;
    }
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    selectionSort(arr, n);

    printf("排序后的数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

这三个排序方法虽说是常用,但是每次使用时都需要自己进行编码,导致代码过长,不够直观,此时我们需要引入系统自带的排序函数--qsort函数。

2.qsort排序

void qsort(void * base, size_t  num, size_t  size, int (*compar)(const void *, const void *))

这里简要介绍一下`qsort`函数的各个参数:
base:指向要排序的数组或其他数据结构的指针。
num:表示`base`指向的数组中有多少个元素。
size:表示每个元素的大小,以字节为单位。
compar:一个用于比较两个元素的函数指针,它接受两个指向元素的指针,返回一个整数值来指示它们的大小关系。---------负数:表示第一个元素<第二个元素

                                            0:表示第一个元素=第二个元素

                                            整数:表示第一个元素>第二个元素  

事例1:整形数组
#include <stdio.h>
#include <stdlib.h>

// 比较函数,用于升序排序
int compare(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

int main() {
    int arr[] = {10, 5, 8, 2, 7};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的整型数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    qsort(arr, n, sizeof(int), compare);

    printf("排序后的整型数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}
```
事例2:字符串数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 比较函数,用于字符串升序排序
int compare(const void *a, const void *b) {
    return strcmp(*(char**)a, *(char**)b);
}

int main() {
    char *strs[] = {"banana", "apple", "orange", "grape"};
    int n = sizeof(strs) / sizeof(strs[0]);

    printf("排序前的字符串数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%s ", strs[i]);
    }
    printf("\n");

    qsort(strs, n, sizeof(char*), compare);

    printf("排序后的字符串数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%s ", strs[i]);
    }
    printf("\n");

    return 0;
}
事例3:浮点数数组
#include <stdio.h>
#include <stdlib.h>

// 比较函数,用于升序排序
int compare(const void *a, const void *b) {
    return (*(float*)a - *(float*)b > 0) - (*(float*)a - *(float*)b < 0);
}

int main() {
    float arr[] = {3.14, 1.618, 2.718, 0.577};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的浮点数数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%.3f ", arr[i]);
    }
    printf("\n");

    qsort(arr, n, sizeof(float), compare);

    printf("排序后的浮点数数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%.3f ", arr[i]);
    }
    printf("\n");

    return 0;
}

事例4:结构体数组排序 

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

// 定义结构体
typedef struct {
    char name[20];
    int age;
} Person;

// 比较函数,用于按年龄升序排序
int compare(const void *a, const void *b) {
    return ((Person*)a)->age - ((Person*)b)->age;
}

int main() {
    Person people[] = {{"Alice", 25}, {"Bob", 20}, {"Charlie", 30}};
    int n = sizeof(people) / sizeof(people[0]);

    printf("排序前的结构体数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%s (%d) ", people[i].name, people[i].age);
    }
    printf("\n");

    qsort(people, n, sizeof(Person), compare);

    printf("排序后的结构体数组:\n");
    for (int i = 0; i < n; i++) {
        printf("%s (%d) ", people[i].name, people[i].age);
    }
    printf("\n");

    return 0;
}

其实每个qsort比较函数中只是compare内容不同而已,根据要求灵活应变即可

例如:

【问题描述】

编写程序统计一个英文文本文件中每个单词的出现次数(词频统计),并将统计结果按单词字典序输出到屏幕上。

注:在此单词为仅由字母组成的字符序列。包含大写字母的单词应将大写字母转换为小写字母后统计。

【输入形式】

打开当前目录下文件“article.txt”,从中读取英文单词进行词频统计。

【输出形式】

程序将单词统计结果按单词字典序输出到屏幕上,每行输出一个单词及其出现次数,单词和其出现次数间由一个空格分隔,出现次数后无空格,直接为回车。

【样例输入】

当前目录下文件article.txt内容如下:

“Do not take to heart every thing you hear.”

“Do not spend all that you have.”

“Do not sleep as long as you want;”

【样例输出】

all 1

as 2

do 3

every 1

have 1

hear 1

heart 1

long 1

not 3

sleep 1

spend 1

take 1

that 1

thing 1

to 1

want 1

you 3

【样例说明】

按单词字典序依次输出单词及其出现次数。

【评分标准】

通过所有测试点将得满分。

(大家可以先做一下看看思路)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_WORD_LEN 100

// 结构体定义:存储单词及其出现次数
typedef struct {
    char word[MAX_WORD_LEN];
    int count;
} WordCount;

// 比较函数,用于对结构体数组进行排序
int compare(const void *a, const void *b) {
    return strcmp(((WordCount *)a)->word, ((WordCount *)b)->word);
}

// 统计单词出现次数并输出结果
void countWords(FILE *file) {
    // 初始化单词计数数组
    WordCount wordCounts[MAX_WORD_LEN];
    int count = 0;

    // 读取文件内容,统计单词出现次数
    char word[MAX_WORD_LEN];
    int wordIndex = 0;
    int c;
    while ((c = fgetc(file)) != EOF) {
        // 排除数字和标点符号
        if (!isalpha(c)) {
            if (wordIndex > 0) {
                word[wordIndex] = '\0'; // 结束单词
                // 将单词转换为小写形式
                for (int i = 0; word[i]; i++) {
                    word[i] = tolower(word[i]);
                }
                // 检查单词是否已经统计过,若已统计则更新计数,否则添加新的单词统计项
                int found = 0;
                for (int i = 0; i < count; i++) {
                    if (strcmp(wordCounts[i].word, word) == 0) {
                        wordCounts[i].count++;
                        found = 1;
                        break;
                    }
                }
                if (!found) {
                    strcpy(wordCounts[count].word, word);
                    wordCounts[count].count = 1;
                    count++;
                }
                wordIndex = 0; // 重置单词索引
            }
        } else {
            // 将字母添加到单词中
            word[wordIndex++] = c;
        }
    }

    // 对单词计数数组按单词字典序进行排序
    qsort(wordCounts, count, sizeof(WordCount), compare);

    // 输出结果
    for (int i = 0; i < count; i++) {
        printf("%s %d\n", wordCounts[i].word, wordCounts[i].count);
    }
}

int main() {
    // 打开文件
    FILE *file = fopen("article.txt", "r");
    if (file == NULL) {
        fprintf(stderr, "Error opening file.\n");
        return 1;
    }

    // 统计单词出现次数并输出结果
    countWords(file);

    // 关闭文件
    fclose(file);

    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值