知识脉络
- 链表的概念
- 如何构建链表
- 操作链表
1. 链表的概念
1.1 单链表
让我们先假想创建单链表是一个综艺游戏,游戏开始,主持人A去路边随机抓人加入游戏,每当抓到人加入链表这个“游戏”中时,A先生便告诉他一条数字,然后告诉上一个加入游戏的人我们新拉入的人的位置。
让我们用术语来解释这个游戏(也就是链表)所构建的内容:
- 节点\结点(翻译原因了):每一个人+他们所知道的信息(数字+下一个人的位置)
- 第一个加入游戏的人:头节点
- 最后一个加入游戏的人:尾节点
这样我们的图便可以这样画:
虚拟节点:没懂
如此”链表“就构建好了,接下来让我们用代码实现一下。
1.2 单链表的构建
第一步我们先得拉人,也就是创建节点
用c语言这样写:
typedef struct link{
char elem;//数字
struct link *next;//下一个人的位置
}Link;
因为leecode是如下方式,所以后面我们都会改成leecode的方法
struct ListNode {
int val;
struct ListNode *next;
};
用python
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
然后我们便可以尝试连接这些节点(这里需要记录头节点)
struct ListNode* initLink() {
int n;
struct ListNode* p;//头指针
struct ListNode* temp = (struct ListNode*)//创建头节点
temp->val = 0;
temp->next = NULL;
p = temp;//头指针指向头节点
for(int i = 1; i <= 4; i++){
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->val = i;
newNode->next = NULL;
temp->next = newNode;
temp = newNode;
}
}
创建链表
int main(){
struct ListNode* head = initLink();
return 0;
}
操作链表
当人们都加入游戏后,游戏开始,也就是可以开始操作链表了。
1、打印链表
嘉宾B到来,我们给他的任务是记录每一位路人的数字。知道的是,每个人都只知道下一个人的位置,并没有其他信息(比如他的前一个人在哪)。
于是我们想获得全部人的信息,便得从第一位加入的人开始遍历,直到最后一个人。也就是让他说出数字和下一个人的位置,然后记录数据,去下一个位置对下一个人做同样的操作。
这时就知道为什么再链表中要记录头节点了(见创建数组的代码),因为遍历时我们将从他开始。
void printLink(struct ListNode* head) {
struct ListNode* temp = head;
// 只要temp不为空,就打印他的数字和下一个人的位置
while(temp != NULL){
printf("%d ", temp->val);
temp = temp->next;
}
printf("\n");
}
//当然我们也能统计人数(获取链表长度)
int32_t getLinkLength(struct ListNode* head) {
struct ListNode* temp = head;
int32_t count = 0;
while(temp != NULL){
count++;
temp = temp->next;
}
return count;
测试效果
int main(){
struct ListNode* head = initLink();//initLink()见上文
printLink(head);
return 0;
}
2、插入链表
嘉宾B得到的任务是拉入路人继续壮大“链表”。可以想到在链表最后加入,也只用重复创建链表时的操作便行,那如果要让这个人加入链表的任意位置呢?甚至是表头呢?
可以想到,新加入一个人时,只用告诉最多两个人就行。
情况一:表头(就是让新人当第一个人),就只告诉他数字和下一个人(以前表头)的位置。
不需要告诉原来表头先生,因为他并不需要知道前一个人的位置
情况二:表尾(就是让新人当最后一个人),就只告诉他数字,再告诉以前的表尾他的位置。
情况三:中间(就是让新人当中间人),这时用图比较好理解。假如加入的人叫Y,让X告诉Y:他原本知道的Z的位置,然后告诉X忘记Z吧,记住Y的位置。
让我们用代码来描述一下
struct ListNode* insertNode(struct ListNode* head, struct ListNode* node,int32_t position){
if(head == NULL){
// 空链表,直接返回新加入的节点
return node;
}
int size = getLinkLength(head);//获取长度
if(position > size){
// 插入的位置大于链表长度,直接返回
return head;
}
// 插入头部
if(position == 1){
node->next = head;
head = node;//更改头节点
return head;
}
struct ListNode* temp = head;
int32_t count = 1;
//找到插入位置的前一个节点
while(count < position-1){
temp = temp->next;
count++;
}
//与两人沟通
nodeInsert->next = temp->next;
temp->next = node;
return head;
测试一下
void testInsert(){
struct ListNode* head = NULL;
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->val = 1;
// 插入第一个元素,现在链表是[1]
head = insertLink(head, node);
printLink(head);
//插入第二个元素,现在链表是[1,2]
node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->val = 2;
head = insertLink(head, node,2);
printLink(head);
//中间插入一个3,插入后链表是[1,3,2]
node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->val = 3;
head = insertLink(head, node,3);
printLink(head);
}
}
删除链表
我们搞了这么久,天都黑了,这下有人不耐烦要离开了。(删除节点)
这时还是分情况:
一、最先来的人(头节点)想离开
二、中间某个人想离开,还是用图表示
A,B,C三人,B想离开,于是他把C的位置告诉了A,然后就离开了
三、最后来的人(尾节点)想离开
struct ListNode* deleteLink(struct ListNode* head,int position){
if(head == NULL || position < 1){
//不存在的人无法离开
return head;
}
int size =getLength(head);
if(position > size){
printf("输入有误")
return head;
}
if(position == 1){
//头节点离开
struct ListNode* temp = head;
head = head->next;
free(temp);//释放内存
return head;
}else{
//中间某个人离开
struct ListNode* temp = head;
int count = 1;
while(count < position-1){
temp = temp->next;
count++;
}
struct ListNode* temp2 = temp->next;
temp->next = temp2->next;
free(temp2);
return head;
}
}
测试一下
void testDeleteLink(){
struct ListNode* p = NULL
//创建0-9的链表
p = initLink(p);
printLink(p);
// 删除头节点
p = deleteLink(p,1);
printLink(p);
// 删除中间节点
p = deleteLink(p,5);
printLink(p);
//删除尾节点
p = deleteLink(p,9);
printLink(p);
}
如此第一关便结束了,以后会继续迭代的,欢迎关注