自学数据结构之单链表

链表

数组和链表对比:

数组是顺序存储,空间有限,不好变化。而链表是链式存储,空间无限,且删除增长方便。但由于链表查找数据需要遍历,所以数组效率比链表高

单链表

结点:数据域和指针域

N个结点通过指针域相互链接组成一个链表

链表中第一个元素所在的结点称为首元结点,首元结点前还有一个结点,称为头结点(不是必须有的,但必须有头指针)。头结点数据域为空,头指针指向头结点或首元结点的数据域,如果头结点指针域为NULL,则表明此链表为空表

概念:开始有头指针,数据元素+指向下一个结点元素的指针,直到结尾指针指向NULL结束(没有指向的空间)

定义一个单链表结点类型:

//定义结点类型
typedef struct Node {
    int data;       //数据域。数据类型,你可以把int型的data换成任意数据类型,包括结构体struct等复合类型
    struct Node *next;          //单链表的指针域
} Node,*LinkedList;  
//Node表示结点的类型,LinkedList表示指向Node结点类型的指针类型,一般用于返回链表的函数(链表就是结点的指针)

初始化:

LinkedList listinit(){
    Node *L; //结点指针为链表
    L=(Node*)malloc(sizeof(Node));      //开辟空间 
    if(L==NULL){                     //判断是否开辟空间失败,这一步很有必要
        printf("申请空间失败");
        //exit(0);                  //开辟空间失败可以考虑直接结束程序
    }
    L->next=NULL;       //指针指向空
    
    return L;
} 
//就可以生成一个空链表

头插法和尾插法

头插法:插入到头结点后面

//单链表的建立1,头插法建立单链表
LinkedList LinkedListCreatH() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申请头结点空间
    L->next = NULL;                      //初始化一个空链表
  
    int x;                         //x为链表数据域中的数据
    scanf("%d",&x);
    while(x != 9999)   {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申请新的结点
        p->data = x;                     //结点数据域赋值
        p->next = L->next;     //头结点指针域赋值给新结点指针
        L->next = p;  //头结点指针指向整个新节点(开头是数据域)
        scanf("%d",&x);
    }
    return L;
}

图示过程:

在这里插入图片描述

尾插法:插入到表尾后面

先建立一个空单链表

尾指针r指向头结点

  • 生成一个新结点§
  • 读入数据到p
  • 将新结点插入到r结点之后 r->next = p;
  • r指向新的尾结点 r=p;
//单链表的建立2,尾插法建立单链表
 
LinkedList LinkedListCreatT() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申请头结点空间
    L->next = NULL;                  //初始化一个空链表
    Node *r;
    r = L;                          //r始终指向终端结点,开始时指向头结点
    int x;                         //x为链表数据域中的数据
    scanf("%d",&x);
    while(x != 9999)  
    {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申请新的结点
        p->data = x;                     //结点数据域赋值
        r->next = p;                 //将结点插入到表尾后
        r = p;		//r指向新结点
        
        scanf("%d",&x);
    }
    r->next = NULL;
 
    return L;
}

在这里插入图片描述

遍历

思路就是建立一个指向链表L的结点,然后沿着逐个向后搜索即可

//便利输出单链表
void printList(LinkedList L){
    Node *p=L->next;
    int i=0;
    while(p){
        printf("第%d个元素的值为:%d\n",++i,p->data);//L->next->data(下一个结点数据)
        p=p->next; //p = L -> next -> next ...
    }
}

修改

//链表内容的修改,再链表中修改值为x的元素变为为k。 建立新结点遍历到更改的位置就行
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
    Node *p=L->next;
    int i=0;
    while(p){
        if(p->data==x){
            p->data=k;
        }
        p=p->next;
    }
    
    	if (p == NULL)
	{
		printf("未找到%d,请确认后再修改!\n",x);
		exit(0);
	}
    return L;
}

插入

循环查找到前驱结点(前一个),再插入

//单链表的插入,在链表的第i个位置插入x的元素
  
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
    Node *pre;                      //pre为前驱结点
    pre = L;
    for (int tempi = 1; tempi < i; tempi++) {
        pre = pre->next;                 //查找第i个位置的前驱结点
    }
    Node *p;                                //插入的结点为p
    p = (Node *)malloc(sizeof(Node));
    p->data = x;
    p->next = pre->next; //第i个位置给新结点指针
    pre->next = p; //前驱结点后面插入该新结点
  
    return L;
}

删除

建立前驱结点和当前结点,当要删除数据时,直接将前驱结点跳过删除结点指向删除结点后一个结点,再将原有结点释放

//单链表的删除,在链表中删除值为x的元素
  
LinkedList LinkedListDelete(LinkedList L,int x) {
    Node *p,*pre;                   //pre为前驱结点,p为查找的结点。
    p = L->next; //头指针赋给p
     
    while(p->data != x) {              //查找值为x的元素
        pre = p;
        p = p->next;
    }
    pre->next = p->next;          //删除操作,将其前驱next指向其后继。
    free(p);
     
    return L;
}

释放链表内存

void LinkListFree(LinkList L) {
	Node* p = L->next;
	while (p) {
		Node* pNext = p->next; //缓存后一个结点,再删除原位置结点
		free(p);
		p = pNext;
	}
	free(L);
}

完整代码:

    while(scanf("%d",&x) != EOF)  //while(1){if(scanf("%d",&x) != EOF){}} 一直死循环 不管写入成功与否
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
 
//定义结点类型
typedef struct Node {
    int data;           //数据类型,你可以把int型的data换成任意数据类型,包括结构体struct等复合类型
    struct Node *next;          //单链表的指针域
} Node,*LinkedList;
 
//单链表的初始化
LinkedList LinkedListInit() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申请结点空间
    if(L==NULL){    //判断申请空间是否失败
        exit(0);    //如果失败则退出程序
    }
    L->next = NULL;          //将next设置为NULL,初始长度为0的单链表
    return L;
}
 
 
//单链表的建立1,头插法建立单链表
LinkedList LinkedListCreatH() {
	Node *L;
	L = (Node *)malloc(sizeof(Node));   //申请头结点空间
	L->next = NULL;                      //初始化一个空链表

	int x;                         //x为链表数据域中的数据
	scanf("%d",&x);
	while (x != 9999) {
		Node *p;
		p = (Node *)malloc(sizeof(Node));   //申请新的结点
		p->data = x;                     //结点数据域赋值
		p->next = L->next;     //头结点指针域赋值给新结点指针
		L->next = p;  //头结点指针指向整个新节点(开头是数据域)
		scanf("%d", &x);
	}
	return L;
}
 
 
LinkedList LinkedListCreatT() {
    Node *L;
    L = (Node *)malloc(sizeof(Node));   //申请头结点空间
    L->next = NULL;                  //初始化一个空链表
    Node *r;
    r = L;                          //r始终指向终端结点,开始时指向头结点
    int x; //x为链表数据域中的数据
	scanf("%d",&x);
    while(x != 9999)  
    {
        Node *p;
        p = (Node *)malloc(sizeof(Node));   //申请新的结点
        p->data = x;                     //结点数据域赋值
        r->next = p;                 //将结点插入到表头L-->|1|-->|2|-->NULL
        r = p;
        
        scanf("%d",&x);
    }
    r->next = NULL;
 
    return L;
}
 
 
//单链表的插入,在链表的第i个位置插入x的元素
 
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
    Node *pre;                      //pre为前驱结点
    pre = L;
    int tempi = 0;
    for (tempi = 1; tempi < i; tempi++) {
        pre = pre->next;                 //查找第i个位置的前驱结点
    }
    Node *p;                                //插入的结点为p
    p = (Node *)malloc(sizeof(Node));
    p->data = x;
    p->next = pre->next;
    pre->next = p;
 
    return L;
}
 
 
//单链表的删除,在链表中删除值为x的元素
 
LinkedList LinkedListDelete(LinkedList L,int x) {
    Node *p,*pre;                   //pre为前驱结点,p为查找的结点。
    p = L->next;
     
    while(p->data != x) {              //查找值为x的元素
        pre = p;
        p = p->next;
    }
    pre->next = p->next;          //删除操作,将其前驱next指向其后继。
    free(p);
     
    return L;
}
 
//链表内容的修改,再链表中修改值为x的元素变为为k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
    Node *p=L->next;
    int i=0;
    while(p){
        if(p->data==x){
            p->data=k;
        }
        p=p->next;
    }
    return L;
}
 
 
//建立新结点遍历输出单链表
void printList(LinkedList L){
    Node *p=L->next;
    int i=0;
    while(p){
        printf("第%d个元素的值为:%d\n",++i,p->data);
        p=p->next;
    }
} 
 
int main() {
    //创建 
    LinkedList list;
    printf("请输入单链表的数据:以9999结尾\n");
    list = LinkedListCreatT();
    //list=LinkedListCreatT();
    printList(list);
     
    //插入 
    int i;
    int x;
    printf("请输入插入数据的位置:");
    scanf("%d",&i);
    printf("请输入插入数据的值:");
    scanf("%d",&x);
    LinkedListInsert(list,i,x);
    printList(list);
     
    //修改
    printf("请输入修改的数据:");
    scanf("%d",&i);
    printf("请输入修改后的值:");
    scanf("%d",&x);
    LinkedListReplace(list,i,x);
    printList(list);
     
    //删除 
    printf("请输入要删除的元素的值:");
    scanf("%d",&x);
    LinkedListDelete(list,x);
    printList(list);
 
    return 0;
}

详细内容请看单链表的基本操作及C语言代码实现 - 数据结构与算法教程 - C语言网 (dotcpp.com)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值