C语言--链表(静态、动态)& 链表合并练习

1.1 概念

链表是由一系列节点组成的,每个节点包含两个域,一个域是数据域,数据域用来保存数据;另一个是指针域,保存下一个节点的地址,链表在内存中是非连续的。

1.2 特点

链表在指定位置插入和删除不需要移动元素,只需要修改指针即可。

查找效率相对于数组会低一些。

链表相对于数组来讲,多了指针域空间开销。

拿到链表的第一个节点就相当于拿到整个链表

1.3 分类

1.3.1 分类方式一

  • 静态链表

    所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放,这种链表成为“静态链表”。

    代码示例:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    // 链表结点类型定义
    struct linkNode{
        int data;
        struct linkNode *next;
    };
    
    void test(){
        struct linkNode node1 = {10, NULL};
        struct linkNode node2 = {20, NULL};
        struct linkNode node3 = {30, NULL};
        struct linkNode node4 = {40, NULL};
        struct linkNode node5 = {50, NULL};
        struct linkNode node6 = {60, NULL};
        
        // 将链表串连起来
        node1.next = &node2;
        node2.next = &node3;
        node3.next = &node4;
        node4.next = &node5;
        node5.next = &node6;
        
        // 遍历链表
        // 先定义一个辅助指针变量
        struct linkNode *pCurrent = &node1;
        while(pCurrent != NULL){
            printf("%d ",pCurrent->data);
            // 指针移动到下一个元素的首地址
            pCurrent = pCurrent->next;
        }
    }
    
    int main(){
        test();
        return 0;
    }
  • 动态链表(创建、遍历、插入、清空、删除、销毁)

        所谓“动态链表”,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结    点和输入各结点数据,并建立起前后相继的关系

   代码示例:

   头文件:LinkList.h

// 定义数据结构
#define _CRT_SECURE_NO_WARNINGS
​
#pragma once  // 防止头文件重复包含
#include<stdlib.h>
#include<malloc.h>
#include<stdio.h>
​
 // 定义宏
#ifdef __cplusplus
extern "C"{
#endif
​
    // 定义结点数据类型
    struct LinkNode{
        int data;
        struct LinkNode *next;
    };
​
    // 初始化链表
    struct LinkNode *Init_LinkList();
    // 在值为oldval的地方插入一个新值newval,原数据往后挪
    void InsertByValue_LinkList(struct LinkNode *header,int oldval,int newval);     // 通过值来插入数据(链表,旧值,新值)
    // 删除值为val的结点    
    void RemoveByValue_LinkList(struct LinkNode *header,int delval);    // 传入链表和要删除的值
    // 遍历
    void Foreach_LinkList(struct LinkNode *header);
    // 销毁链表(无法插入数据)
    void Destory_LinkList(struct LinkNode *header);
    // 清空(还能插入数据)
    void Clear_LinkList(struct LinkNode *header);
​
#ifdef __cplusplus
}
#endif

LinkList.c

        

#include"LinkList.h"


// 初始化列表
struct LinkNode *Init_LinkList(){
	int val = -1;
	// 创建头节点
	struct LinkNode *header = (struct LinkNode*)malloc(sizeof(struct LinkNode));
	// 尾部的指针
	struct LinkNode *pRear = header;

	header->data = -1;
	header->next = NULL;
	
	while(1){
		// 先创建结点
		struct LinkNode *newnode = (struct LinkNode*)malloc(sizeof(struct LinkNode));
		printf("输入插入数据:\n");
		scanf("%d",&val);
		if(val==-1){
			break;
		}
		
		newnode->data = val;
		newnode->next = NULL;

		// 新结点插入到链表中
		pRear->next = newnode;

		// 更新尾部指针指向
		pRear = newnode;
	}

	return header;   // 返回头结点   相当于返回链表

}

// 在值为oldval的后面插入一个新的数据newval
void InsertByValue_LinkList(struct LinkNode *header,int oldval,int newval){     // 通过值来插入数据(链表,旧值,新值)
	// 定义两个辅助指针变量
	struct LinkNode *pPrev = header;
	struct LinkNode *pCurrent = pPrev->next;

	// 创建新结点
	struct LinkNode *newnode = (struct LinkNode*)malloc(sizeof(struct LinkNode));
	
	if(NULL == header){
		return;    // 如果链表不存在,直接返回
	}

	while (pCurrent != NULL){
		if(pCurrent->data == oldval){   // 如果找到了传递过来的旧值,停止循环
			break;
		}
		// 如果没找到,pPrev和pCurrent各向后挪一位
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}

#if 0        // 当找不到oldval时,添加的值放在链表的尾部
	// 如果pCurrent为空,说明不存在值为oldval的结点
	if(pCurrent == NULL){
		return;
	}
#endif

	newnode->data = newval;
	newnode->next = NULL;

	// 新结点插入到链表中
	newnode->next = pCurrent;
	pPrev->next = newnode;


}   

// 删除值为val的结点    
void RemoveByValue_LinkList(struct LinkNode *header,int delval){        // 传入链表和要删除的值
	
	// 两个父组指针变量
	struct LinkNode *pPrev = header;
	struct LinkNode *pCurrent = pPrev->next;

	if(NULL == header){
		return;    // 如果链表不存在,直接返回
	}

	while(pCurrent != NULL){
		if(pCurrent->data == delval){
			break;
		}
		
		// 移动两个指针
		pPrev = pCurrent;
		pCurrent = pCurrent->next;
	}

	if(NULL == pCurrent){
		return;
	}

	// 重新建立待删除结点的前驱和后继结点关系
	pPrev->next = pCurrent->next;
	// 释放删除结点内存
	free(pCurrent);
	pCurrent = NULL;
} 

// 遍历

void Foreach_LinkList(struct LinkNode *header){
	// 定义辅助指针变量
	struct LinkNode *pCurrent = header->next;

	if(NULL == header){
		return;    // 如果链表不存在,直接返回
	}

	
	//printf("%d ",pCurrent->data);
	while(pCurrent != NULL){
		printf("%d ",pCurrent->data);    // 输出当前节点的数据
		pCurrent = pCurrent->next;   // 指针后移
	}

}

// 销毁链表(无法插入数据)
void Destory_LinkList(struct LinkNode *header){

	// 定义辅助指针变量
	struct LinkNode *pCurrent = header->next;

	if(NULL == header){
		return;    // 如果链表不存在,直接返回
	}

	while(pCurrent != NULL){
		// 先保存当前结点的下一结点的地址
		struct LinkNode *pNext = pCurrent->next;

		// 释放当前结点内存
		free(pCurrent);

		// 指针向后移动
		pCurrent = pNext;
	}
}

// 清空(还能插入数据,只剩一个头结点)
void Clear_LinkList(struct LinkNode *header){
	// 定义辅助指针变量
	struct LinkNode *pCurrent = header->next;

	if(NULL == header){
		return;    // 如果链表不存在,直接返回
	}

	while(pCurrent != NULL){
		// 保存当前结点的下一个结点地址
		struct LinkNode *pNext = pCurrent->next;

		// 释放当前结点内存
		free(pCurrent);

		// pCurrent指向下一个结点
		pCurrent = pNext;
	}

	// 清空最后一个数据
	header->next = NULL; 
}

 TestLinkList.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include"LinkList.h"

void test(){
	// 初始化链表
	struct LinkNode *header = Init_LinkList();
	// 打印链表
	Foreach_LinkList(header);
	// 插入数据
	InsertByValue_LinkList(header,1000,666);
	// 打印链表
	printf("\n----------------\n");
	Foreach_LinkList(header);
	// 清空链表
	Clear_LinkList(header);
	// 打印链表
	printf("\n----------------\n");
	Foreach_LinkList(header); 
	// 插入数据
	InsertByValue_LinkList(header,1000,111);
	// 插入数据
	InsertByValue_LinkList(header,1000,222);
	// 插入数据
	InsertByValue_LinkList(header,1000,333);
	// 打印链表
	printf("\n----------------\n");
	Foreach_LinkList(header); 

	RemoveByValue_LinkList(header,222);
	// 打印链表
	printf("\n----------------\n");
	Foreach_LinkList(header); 

	// 销毁链表
	Destory_LinkList(header);

}


int main(){
	test();

	system("pause");
	return EXIT_SUCCESS;
}

链表合并

已有a、b两个链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,按学号升序排列。

输入:第一行,a、b两个链表元素的数量N、M,用空格隔开。 接下来N行是a的数据 然后M行是b的数据 每行数据由学号和成绩两部分组成

输出:按照学号升序排列的数据

样例输入

2 3    // a链表的元素数量 b链表的元素数量
5 100  // a链表中的元素
6 89   // a
3 82    // b链表中的元素
4 95    // b
2 10    // b

样例输出:

2 10
3 82
4 95
5 100
6 89

代码演示:

#include<stdio.h>
#include<malloc.h>
​
struct student{   // 设计结点
    double number;   // 数据域--学号
    double score;       // 数据域--成绩
    struct student *next;    // 指针域
};
​
struct student *create(int n);     // 创建链表函数
​
void output(struct student *header);   // 链表输出函数
​
void order(struct student *header1,struct student *header2);   // 链表排序函数
​
int main()
{
    int n,m;   // n为a链表的元素数量,m为b链表中的元素的数量
    struct student *header1,*header2;   // 创建两个链表的头结点 
    
    if(scanf("%d %d",&n,&m) == 2){   // 对数量值进行输入
        header1 = create(n);    // 创建链表a  元素数量为n个
        header2 = create(m);    // 创建链表b  元素数量为m个
        order(header1,header2); // 对两个链表进行排序
    }
    return 0;
}
​
struct student *create(int n){     // 创建链表函数
    struct student *header;    // 创建头结点指针
    header = (struct student*)malloc(sizeof(struct student));  // 创建头结点
    header->next = NULL;    // 将头结点的指针域指向NULL
    struct student *q = header;    // 创建指针q,指向头结点
    struct student *p;          // 创建结点指针p
    
    for(int i=0;i<n;i++){     // 遍历数量
        p = (struct student*)malloc(sizeof(struct student));   // 创建结点
        if(scanf("%lf %lf",&(p->number),&(p->score)) == 2){   // 给结点输入学号和成绩
            p->next = NULL;     // 采用尾插法插入结点
            q->next = p;   // 第一轮里把头结点的指针域指向了新建的结点p
            q = p;      // 将p赋值给q,以便进行下一次循环
        }
    }
    
    return(header);    // 返回链表
}
​
void output(struct student *header){     // 输出链表每个结点的信息
    header = header->next;    // 将header指向下一个结点 
    struct student *q;     // 定义一个结点指针(辅助)
    while(header != NULL){    // 当结点存在时进行打印
        printf("%.0f %.0f\n",header->number,header->score);   // 打印结点数据
        q = header;    // 记录下当前结点的指针
        header = header->next;    // 指向下一个结点,以便循环
        free(q);   // 释放内存
    }
}
​
void order(struct student *header1,struct student *header2){   // 链表排序
    struct student *q;    // 定义辅助指针变量
    q = header1;     // 将q指向链表a的头结点
    header2 = header2->next;   // 链表b的结点下移,header2不再是头结点
    
    while(q->next != NULL){   // 如果链表a的结点的指针域不为空
        q = q->next;     // 则将下一个结点赋值给q,直到最后一个结点
    }
    q->next = header2;    // 表a的最后一个结点的next指向链表b的首结点完成连接
    q = header1->next;   // 再次把q指向表a的头结点
    
    struct student *min;   // 定义指向最小学号的指针
    
    int t;    // 保存最小学号的变量
    int n,g;   // 交换学号、成绩的中间变量
    struct student *p;    // 定义辅助指针变量p
    while(q != NULL){    // 开始遍历,排序选择最外层
        p = q;     // 令p等于查找的结点q
        t = p->number;   // 令t等于开始查找的第一个结点的学号
        min = p;  // 令最小学号结点指针指向p
        while(p!=NULL){     // 如果p不为空
            if(p->number < t){    // 判断当前遍历的结点的学号是否小于第一个结点的学号(也就是预设的最小学号)
                t = p->number;   // 如果小于,就把t的值换成小于它的p->number
                min = p;   // 将最小学号的结点换成p
            }
            p = p->next;  //  如果不是,则把p往后挪
        }
        // 交换学号
        n = q->number;    // n等于第一个结点的学号
         // 将最小学号结点的学号与第一个结点的学号进行交换
        q->number = min->number;  
        min->number = n;
        
        // 同理交换成绩
        g = q->score;
        q->score = min->score;
        min->score = g;
        
        // 第一遍排序完后q后移,也就是选择排序开始排第二遍时的第一个数
        q = q->next;
    }
    output(header1);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值