题目 | 考点 | 分值 | 难度 |
---|---|---|---|
19冬 2-Block Reversing | 25 | ||
19秋 2-Merging Linked Lists | 25 | ||
A1032 Sharing | 25 | ||
A1052 Linked List Sorting | 25 | ||
A1074 Reversing Linked List | 25 | ||
A1097 Duplication on a Linked List | 25 |
1.链表的概念
结点的定义
// 结点的定义
sturct node {
typename data; // 数据域
node* next; // 指针域
};
《算法笔记》中统一为 带头结点的写法。
带头结点的链表:头结点称为 head,数据域data不存放内容,指针域next 指向第一个数据域有内容的结点(第一个结点)
示例:
2.为 链表结点 分配内存空间
1.malloc函数
malloc 是C语言中 stdlib.h 头文件下用于申请动态内存的函数,其 返回类型 是 申请的同变量类型的指针。
typename* p = (typename*)malloc(sizeof(typename));
示例
int* p = (int*)malloc(sizeof(int));
node* p = (node*)malloc(sizeof(node));
含义:malloc函数向内存申请一块大小为 sizeof(int) 的空间,并且 返回 指向这块空间的指针,因此时这个指针是一个未确定类型的指针 void*,因此需要强制转换为 int* 类型的指针。
若申请失败,则返回空指针 null。
2.new运算符
new 是C++中用来申请动态空间的运算符,返回类型同样是申请的同变量类型的指针。
typename* p = new typename;
示例
int* p = new int;
node* p = new node;
3.内存泄露
指使用 malloc 与 new 开辟出来的内存空间在使用过后没有释放,导致其在程序结束之前时钟占据该内存空间。
(1) free函数
对应 malloc函数。只需在 free的参数中填写需要释放的内存空间的指针变量即可。
free(p);
实现效果:①释放指针变量p所指向的内存空间;②将指针变量p指向空地址NULL。
(2) delete运算符
对应 new运算符。
delete(p);
3.链表的基本操作
1.创建链表
①创建头结点,并指向null;
②pre 指向 当前结点;
③循环:新建结点 p,赋值数据域data,指针域设为null,尾插到链表中(pre->next = p),前驱结点指向当前结点 为了下一次插入。
④返回头结点。
图示:
代码:
#include <stdio.h>
#include <stdlib.h>
// 结点定义
struct node {
int data; // 数据域
node* next; // 指针域
};
// 创建链表
node* create(int a[]) {
node *pre, *p, *head;
head = new node; // 创建头结点
head->next = NULL;
pre = head; // 指向头结点,便于尾插
for (int i = 0; i < 5; i++) {
p = new node; // 创建结点p
p->data = a[i]; // 数据域
p->next = NULL; // 指针域
pre->next = p; // 插入到链表中
pre = p; // 指针右移
}
return head; // 返回头结点
}
int main() {
int a[5] = { 3, 1, 4, 5, 2 };
node* L = create(a); // 返回头结点
L = L->next; // 指针指向第一个有数据域的结点
while (L) { // 若当前结点不为空
printf("%d ", L->data);
L = L->next;
}
return 0;
}
2.查找元素
①新建结点指向头结点的下一个结点;
②while循环,依次访问当前结点的数据域,若==x,cnt++;
③访问完当前结点之后,指针指向下一个结点。
int search(node* head, int x) {
int cnt = 0;
node *p = head->next;
while (p) {
if (p->data == x)
cnt++;
p = p->next;
}
return cnt;
}
3.插入元素
①定义指针p指向头结点head;
②指针p后移到待插入结点的前一个结点;
③新建结点q,数据域为待插入值,指针域指向上一个结点的下一个结点;
④上一个结点p指针指向这个结点q。
图示:
代码:
void insert(node* head, int pos, int x){
node* p = head;
// 找到插入位置的前一个结点,并用 指针p指向
for(int i = 0; i < pos-1; i++){
p = p->next;
}
// 新建结点,并用 指针q指向
node* q = new node;
q->data = x;
q->next = p->next;
p->next = q;
}
4.删除元素
①设置一个前驱结点pre指向头结点,设置一个当前节点p指向头结点的下一个结点;
②只要当前结点p不为null,就往后遍历;
③如果当前结点p->data == x,让前驱结点pre直接指向当前结点的后一个结点p->next,删除当前结点;
④如果当前结点不等于x,往后遍历。
void del(node* head, int x){
node* pre = head;
node* p = head->next;
while(p != NULL){
if(p->data == x){
pre->next = p->next;
delete(p);
p = pre->next;
}
else{
pre = p;
p = p->next;
}
}
}
Leetcode总结的删除操作
代码:
ListNode* removeElements(ListNode* head, int val) {
// 建立虚拟头结点
ListNode* dummy = new ListNode(0, head);
// 遍历用指针
ListNode* cur = dummy;
// 遍历链表
while(cur->next != nullptr){
if(cur->next->val == val){ // 删除链表结点
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}else{
cur = cur->next;
}
}
// 返回头结点
return dummy->next;
}
静态链表的通用解题步骤
①定义静态链表
struct Node{
int address; // 当前结点的地址
int data; // 数据域
int next; // 指针域,指向下一个结点的地址
XXX; // 结点的某个性质,不同的题目有不同的设置,详见例题
}node[100010];
②初始化
对XXX初始化,定义为正常情况下达不到的数字
for(int i = 0; i < maxn; i++){
node[i].XXX = 0;
}
③遍历整条链表
int p = begin, count = 0;
while(p != -1){
XXX = 1;
count++;
p = node[p]->next;
}
④对数组进行排序以把有效结点移到数组左端,这样就可以用步骤3得到的count来访问它们。
bool cmp(Node a, Node b){
if(a.XXX == -1 || b.XXX == -1){
return a.XXX > b.XXX;
}else{
// 第二级排序
}
}