数据结构学习——单链表的创建及其基本操作

数据结构学习——单链表的创建及其基本操作

什么是链表?

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

——百度百科

链表就是一种链式存储的线性表,它由一系列节点组成每个节点包含数据部分指向下一个节点的指针

单链表的创建

节点定义

在C语言中,链表节点通常通过结构体来定义,结构体包含至少两个部分:数据域和指针域。数据域用于存储数据,而指针域用于链接到下一个节点。节点的内存空间通常通过malloc函数动态分配。

typedef int ElemType;//定义数据类型  Element Type 元素 类型
typedef struct node {
	ElemType data;//数据域
	struct node* next;//指针域
}Node;//Node 节点

初始化单链表

初始化单链表通常涉及创建一个头节点,并将其指针域设置为NULL,表示链表为空。

Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));//分配头节点内存
	head->data = 0;//将头节点数据域设置为0
	head->next = NULL;//指针域设置为NULL
	return head;//返回创建的头节点
}//初始化单链表,返回头节点   Initialize 初始化

代码如下,现在一个单链表就创建好了,但它现在只是一个空表,没有存储任何数据

#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node;
Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
int main() {
	Node* head = initList();
	return 0;
}

在这里插入图片描述

单链表的基本操作

遍历单链表

方便检查链表存储情况,该操作将链表存储数据依次输出

void listNode(Node* head) {
	Node* p = head->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

单链表的增加操作

链表的增加操作可以在链表的头部、尾部或指定位置插入新节点。头插法会在链表的头部插入新节点,尾插法会在链表的尾部插入新节点,而指定位置插入则需要先找到目标位置的前一个节点,然后插入新节点。

头插法

头插法是将新节点插入到链表的前端,即头节点之后。这种方法创建的单链表中节点的顺序与输入的顺序相反。具体操作是让新节点指向头节点的下一个节点,让头节点指向新节点,**顺序很重要!!!**如果先让头节点指向新节点,链表就断了。

void insertHead(Node* head, ElemType e) {
    Node* p = (Node*)malloc(sizeof(Node));//创建新节点
    p->data = e;//赋值
    p->next = head->next;//新节点指向头节点的下一个节点
    head->next = p;//头节点指向新节点
}

在这里插入图片描述
在这里插入图片描述

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node;
Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
void listNode(Node* head) {
	Node* p = head->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
void insertHead(Node* head, ElemType e) {
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	newNode->next = head->next;
	head->next = p;
}
int main() {
	Node* head = initList();
	insertHead(head, 1);
	insertHead(head, 2);
	insertHead(head, 3);
	insertHead(head, 4);
	insertHead(head, 5);
	listNode(head);
	return 0;
}

输出结果:

5 4 3 2 1
    
尾插法

尾插法是将新节点插入到链表的末尾。这种方法创建的单链表中节点的顺序与输入的顺序一致。具体操作是先找到尾节点的地址,让尾节点指向新节点,新节点指向NULL,现在新节点就变成了尾节点,返回新的尾节点。

Node* getTail(Node* head) {
	Node* p = head;
	while (p->next != NULL) {
		p = p->next;
	}//遍历链表,直到最后一个节点
	return p;//返回尾节点
}
Node* insertTail(Node* tail, ElemType e) {
    Node* newNode = (Node*)malloc(sizeof(Node));//创建新节点
    newNode->data = e;//赋值
    tail->next = newNode;//尾节点指向新节点
    newNode->next = NULL;//新节点指向空
    return newNode;//返回新节点
}

在这里插入图片描述

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node;
Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
void listNode(Node* head) {
	Node* p = head->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
Node* getTail(Node* head) {
	Node* p = head;
	while (p->next != NULL) {
		p = p->next;
	}
	return p;
}
Node* insertTail(Node* tail, ElemType e) {
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	tail->next = newNode;
	newNode->next = NULL;
	return newNode;
}
int main() {
	Node* head = initList();
	Node* tail = getTail(head);
	tail = insertTail(tail, 1);//尾插法并更新尾节点
	tail = insertTail(tail, 2);
	tail = insertTail(tail, 3);
	tail = insertTail(tail, 4);
	tail = insertTail(tail, 5);
	listNode(head);
	return 0;
}

输出结果:

1 2 3 4 5
    
向指定位置插入

要向指定位置插入,首先要找到指定位置的前驱节点,即指定位置的前一个节点,让新节点指向前驱节点的下一个节点,让前驱节点指向新节点,顺序很重要!!!

void insertPosition(Node* head, int pos, ElemType e) {
    Node* p = head;//用来保存插入位置的前驱节点
    //遍历链表,找到插入位置的前驱节点
    for (int i = 0; i < pos - 1; i++) {
        p = p->next;
        if (p == NULL) {
            printf("插入位置错误\n");
            return;
        }
    }
    Node* newNode = (Node*)malloc(sizeof(Node));//创建新节点
    newNode->data = e;//给新节点赋值
    newNode->next = p->next;//让新节点指向插入位置的节点
    p->next = newNode;//让插入位置的前驱节点指向新节点
}

在这里插入图片描述

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node;
Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
void listNode(Node* head) {
	Node* p = head->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
Node* getTail(Node* head) {
	Node* p = head;
	while (p->next != NULL) {
		p = p->next;
	}
	return p;
}
Node* insertTail(Node* tail, ElemType e) {
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	tail->next = newNode;
	newNode->next = NULL;
	return newNode;
}
void insertPosition(Node* head, int pos, ElemType e) {
	Node* p = head;
	for (int i = 0; i < pos - 1; i++) {
		p = p->next;
		if (p == NULL) {
			printf("插入位置错误\n");
			return;
		}
	}
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	newNode->next = p->next;
	p->next = newNode;
}
int main() {
	Node* head = initList();
	Node* tail = getTail(head);
	tail = insertTail(tail, 1);
	tail = insertTail(tail, 2);
	tail = insertTail(tail, 3);
	tail = insertTail(tail, 4);
	tail = insertTail(tail, 5);
	insertPosition(head, 3, 30);//把30插入在链表的第三个位置
	listNode(head);
	return 0;
}

输出结果:

1 2 30 3 4 5
    

单链表的删除操作

删除节点时,需要先找到待删除节点的前驱节点,然后调整指针,最后释放节点内存。

具体操作如下:

  • 找到要删除节点的前驱节点p
  • 用指针q记录要删除的节点
  • 通过改变p的后继节点实现删除
  • 释放删除节点的空间
void deleteNode(Node* head, int pos) {
    Node* p = head;//用来保存删除位置的前驱节点
    //遍历链表,找到删除位置的前驱节点
    for (int i = 0; i < pos - 1; i++) {
        p = p->next;
        if (p == NULL) {
            printf("删除位置错误\n");
            return;
        }
    }
    //判断删除位置是否为空
    if (p->next == NULL) {
        printf("删除位置错误\n");
        return;
    }
    Node* q = p->next;//q是要删除的节点
    p->next = q->next;//让要删除节点的前驱指向要删除节点的后继
    free(q);//释放删除节点的内存空间
}

在这里插入图片描述

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node;
Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
void listNode(Node* head) {
	Node* p = head->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
Node* getTail(Node* head) {
	Node* p = head;
	while (p->next != NULL) {
		p = p->next;
	}
	return p;
}
Node* insertTail(Node* tail, ElemType e) {
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	tail->next = newNode;
	newNode->next = NULL;
	return newNode;
}
void insertPosition(Node* head, int pos, ElemType e) {
	Node* p = head;
	for (int i = 0; i < pos - 1; i++) {
		p = p->next;
		if (p == NULL) {
			printf("插入位置错误\n");
			return;
		}
	}
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	newNode->next = p->next;
	p->next = newNode;
}
void deleteNode(Node* head, int pos) {
	Node* p = head;
	for (int i = 0; i < pos - 1; i++) {
		p = p->next;
		if (p == NULL) {
			printf("删除位置错误\n");
			return;
		}
	}
	if (p->next == NULL) {
		printf("删除位置错误\n");
		return;
	}
	Node* q = p->next;
	p->next = q->next;
	free(q);
}
int main() {
	Node* head = initList();
	Node* tail = getTail(head);
	tail = insertTail(tail, 1);
	tail = insertTail(tail, 2);
	tail = insertTail(tail, 3);
	tail = insertTail(tail, 4);
	tail = insertTail(tail, 5);
	insertPosition(head, 3, 30);
	deleteNode(head, 3);//删除链表第三个节点
	listNode(head);
	return 0;
}

输出结果:

1 2 3 4 5
    

单链表的修改操作

修改节点数据需要先找到该节点,然后更新其数据部分。

void modifyNode(Node* head, int pos, ElemType new_data) {
	Node* p = head;
	//遍历链表,找到要修改的节点
	for (int i = 0; i < pos; i++) {
		p = p->next;
		if (p == NULL) {
			printf("修改位置错误\n");
			return;
		}
	}
	p->data = new_data;
}

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
	ElemType data;
	struct Node* next;
}Node;
Node* initList() {
	Node* head = (Node*)malloc(sizeof(Node));
	head->data = 0;
	head->next = NULL;
	return head;
}
void listNode(Node* head) {
	Node* p = head->next;
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
Node* getTail(Node* head) {
	Node* p = head;
	while (p->next != NULL) {
		p = p->next;
	}
	return p;
}
Node* insertTail(Node* tail, ElemType e) {
	Node* newNode = (Node*)malloc(sizeof(Node));
	newNode->data = e;
	tail->next = newNode;
	newNode->next = NULL;
	return newNode;
}
void modifyNode(Node* head, int pos, ElemType new_data) {
	Node* p = head;
	for (int i = 0; i < pos; i++) {
		p = p->next;
		if (p == NULL) {
			printf("修改位置错误\n");
			return;
		}
	}
	p->data = new_data;
}
int main() {
	Node* head = initList();
	Node* tail = getTail(head);
	tail = insertTail(tail, 1);
	tail = insertTail(tail, 1);
	tail = insertTail(tail, 1);
	tail = insertTail(tail, 1);
	tail = insertTail(tail, 1);
	modifyNode(head, 3, 3);//将第三个节点的数据修改为3
	listNode(head);
	return 0;
}

输出结果:

1 1 3 1 1 
    

单链表的查找操作

查找节点涉及到遍历链表并比较节点数据,找到匹配的节点后返回该节点。

Node* findNode(Node* head, ElemType data) {
	Node* p = head->next;
	//遍历链表,找到包含指定数据的节点
	while (p != NULL) {
		if (p->data == data) {
			return p;//返回找到的节点
		}
		p = p->next;
	}
	printf("链表中不存在该值\n");
	return NULL;//未找到,返回NULL
}

获取链表长度

遍历链表,计算链表长度

int listLenth(Node* head) {
	Node* p = head->next;
	int len = 0;
	while (p != NULL) {
		len++;
		p = p->next;
	}
	return len;
}

单链表的释放

单链表的释放是将头节点以外的节点释放掉,具体操作如下:

  • 指针p指向头节点后的第一个节点
  • 判断指针p是否为空
  • 如果不为空,用指针q记录指针p的后继节点
  • 释放指针p指向的空间
  • 指针p和指针q指向同一个节点,循环上面的操作
void freeList(Node* head) {
	Node* p = head->next;
	Node* q;
	while (p != NULL) {
		q = p->next;
		free(p);
		p = q;
	}
	head->next = NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值