单链表排序程序
单链表排序程序详细解释
程序结构概述
这个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²)的复杂度来源于双重嵌套循环,每个元素都要与其后面的所有元素进行比较,导致比较次数随输入规模的平方增长。
                    
                
                
            
        
浙公网安备 33010602011771号