单链表排序程序

单链表排序程序详细解释

程序结构概述

这个C程序实现了单链表的创建、冒泡排序和输出功能。程序包含四个主要函数:

  • creatlist(): 创建链表
  • fun(): 对链表进行冒泡排序
  • outlist(): 输出链表内容
  • main(): 主函数

数据结构定义

#define N 6
typedef struct node {
    int data;           // 存储数据
    struct node *next;  // 指向下一个节点的指针
} NODE;
  • N定义为6,表示链表将有6个数据节点
  • NODE结构体定义了链表节点,包含整型数据和指向下一个节点的指针

函数详细分析

1. creatlist函数 - 创建链表

NODE *creatlist(int a[])
{
    NODE *h, *p, *q;
    int i;
    
    // 创建头节点
    h = (NODE *)malloc(sizeof(NODE));
    h->next = NULL;
    
    // 逐个添加数据节点
    for(i=0; i<N; i++) {
        q = (NODE *)malloc(sizeof(NODE));
        q->data = a[i];
        q->next = NULL;
        
        if (h->next == NULL)  
            h->next = p = q;    // 第一个数据节点
        else {
            p->next = q;        // 链接到链表末尾
            p = q;              // 更新尾指针
        }
    }
    return h;
}

工作原理:

  • 创建一个头节点h(不存储实际数据,只作为链表入口)
  • 遍历数组a[],为每个元素创建新节点
  • 使用尾插法将新节点添加到链表末尾
  • 返回头节点指针

链表结构: 头节点 -> 0 -> 10 -> 4 -> 2 -> 8 -> 6 -> NULL

2. fun函数 - 冒泡排序

void fun(NODE *h)
{
    NODE *p, *q;
    int t;
    
    p = h->next;        // p指向第一个数据节点
    while (p) {
        q = p->next;    // q指向p的下一个节点
        while (q) {
            if (p->data > q->data) {
                // 交换数据
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
            q = q->next;
        }
        p = p->next;
    }
}

排序过程分析:

这是冒泡排序的链表版本:

  • 外层循环:p指针遍历每个节点
  • 内层循环:q指针从p的下一个节点开始,与p比较
  • 如果p->data > q->data,交换两个节点的数据值
  • 每轮比较后,较小的值会"冒泡"到前面

排序步骤示例:

初始: 0 -> 10 -> 4 -> 2 -> 8 -> 6
第1轮p=0: 0与后面所有元素比较,0已是最小,无变化
第2轮p=10: 10与4,2,8,6比较,最终变为: 0 -> 4 -> 2 -> 8 -> 6 -> 10
第3轮p=4: 4与2,8,6比较,最终变为: 0 -> 2 -> 4 -> 6 -> 8 -> 10
...
最终: 0 -> 2 -> 4 -> 6 -> 8 -> 10

3. outlist函数 - 输出链表

void outlist(NODE *h)
{
    NODE *p;
    p = h->next;    // 跳过头节点,指向第一个数据节点
    
    if (p == NULL)
        printf("The list is NULL!\n");
    else {
        printf("\nHead ");
        do {
            printf("->%d", p->data);
            p = p->next;
        } while(p != NULL);
        printf("->End\n");
    }
}

输出格式: Head ->0->10->4->2->8->6->End

4. main函数 - 程序入口

void main()
{
    NODE *head;
    int a[N] = {0, 10, 4, 2, 8, 6};
    
    head = creatlist(a);              // 创建链表
    printf("\nThe original list:\n");
    outlist(head);                    // 输出原始链表
    
    fun(head);                        // 排序
    printf("\nThe list after sorting :\n");
    outlist(head);                    // 输出排序后链表
}

程序执行结果

The original list:
Head ->0->10->4->2->8->6->End

The list after sorting :
Head ->0->2->4->6->8->10->End

关键技术点

1. 头节点的作用

  • 简化链表操作,不需要特殊处理第一个数据节点
  • 头节点不存储实际数据,只是作为链表的入口点

2. 指针操作

  • p = h->next:跳过头节点
  • q = p->next:获取下一个节点
  • p->next = q:建立链接关系

3. 内存分配

  • 使用malloc()动态分配内存
  • 每个节点都需要单独分配内存空间

4. 排序算法

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 只交换数据值,不改变节点链接关系

程序特点

优点:

  • 代码结构清晰,功能模块化
  • 使用头节点简化操作
  • 冒泡排序实现简单易懂

可能的改进:

  • 可以添加内存释放函数
  • 可以实现更高效的排序算法
  • 可以添加错误处理机制

这个程序是学习链表操作和排序算法的很好例子,展示了基本的数据结构操作和算法实现。

 

让我详细解释为什么这个链表排序算法的时间复杂度是O(n²)。

时间复杂度分析

1. 算法结构分析

void fun(NODE *h)
{
    NODE *p, *q;
    int t;
    
    p = h->next;        // 外层循环起点
    while (p) {         // 外层循环:遍历每个节点
        q = p->next;    // 内层循环起点  
        while (q) {     // 内层循环:与后续节点比较
            if (p->data > q->data) {
                // 交换操作 O(1)
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
            q = q->next;
        }
        p = p->next;
    }
}

2. 循环次数计算

假设链表有n个数据节点:

  • 第1轮(p指向第1个节点):内层循环执行 n-1
  • 第2轮(p指向第2个节点):内层循环执行 n-2
  • 第3轮(p指向第3个节点):内层循环执行 n-3
  • ...
  • 第n-1轮(p指向第n-1个节点):内层循环执行 1
  • 第n轮(p指向第n个节点):内层循环执行 0 次(直接结束)

3. 总比较次数

总的比较次数 = (n-1) + (n-2) + (n-3) + ... + 2 + 1

这是一个等差数列求和:

总次数 = (n-1) × n / 2 = (n² - n) / 2

4. 具体例子

以程序中的6个元素为例:

第1轮:p=0,与10,4,2,8,6比较 → 5次比较
第2轮:p=10,与4,2,8,6比较 → 4次比较  
第3轮:p=4,与2,8,6比较 → 3次比较
第4轮:p=2,与8,6比较 → 2次比较
第5轮:p=8,与6比较 → 1次比较
第6轮:p=6,无后续节点 → 0次比较

总计:5+4+3+2+1 = 15次比较
验证:(6²-6)/2 = 30/2 = 15 ✓

5. 时间复杂度推导

T(n) = (n² - n) / 2
     = n²/2 - n/2
     = O(n²)

当n足够大时:

  • n²项占主导地位
  • 常数系数1/2被忽略
  • 低阶项-n/2被忽略

因此时间复杂度为O(n²)

6. 为什么是平方级复杂度?

这是因为算法使用了嵌套循环

  • 外层循环执行约n次
  • 内层循环平均执行约n/2次
  • 总执行次数:n × (n/2) = n²/2 = O(n²)

7. 与数组冒泡排序的对比

数组版本的冒泡排序时间复杂度也是O(n²):

for(i=0; i<n-1; i++)           // 外层循环n-1次
    for(j=0; j<n-1-i; j++)     // 内层循环递减
        if(a[j] > a[j+1])      // 比较相邻元素
            swap(a[j], a[j+1]);

链表版本本质上是相同的算法,只是用指针代替了数组索引,所以时间复杂度相同。

总结: O(n²)的复杂度来源于双重嵌套循环,每个元素都要与其后面的所有元素进行比较,导致比较次数随输入规模的平方增长。

posted @ 2025-08-31 15:57  量子我梦  阅读(1)  评论(0)    收藏  举报