一.链表的概念
1.分为数据域和指针域:
struct node {
typename datd; //数据域
node* next; //指针域
};
2.链表分为带头结点和不带头结点(头结点:data 不存放数据,next 指向第一个数据域有内容的结点);
二.为新结点申请动态内存空间
1.malloc函数:(C语言)
头文件:#include<stdlib.h>
;
返回类型:同变量类型的指针;
基本用法:typename* p = (typename*)malloc(sizeof(typename));
示例:
int* p = (int*)malloc(sizeof(int));
malloc函数向内存申请一块大小为 sizeof(int) 的空间,并返回指向这块空间的指针,此时这个指针是未确定类型的 void*,在最前面加上(int*) 强制转换成 int*;
申请失败:返回空指针NULL并赋值给 p(当申请了较大的动态数组时);
2.new运算符:(C++)
基本用法:typename* p = new typename;
示例:
int* p = new int;
申请失败:启动 C++ 异常处理机制而不是返回空指针 NULL;
3.内存泄漏
指使用 malloc 和 new 开辟出来的内存空间在使用后没有释放,导致后来无内存可分配;
如何释放空间:
1).free函数(对应 malloc函数):释放内存空间,并将 p 指向空地址 NULL;
free(p);
2).delete运算符(对应 new运算符):
delete(p);
三.基本操作
1.创建链表:
#include<stdio.h>
#include<stdlib.h>
struct node {
int data;
node* next;
};
node* create(int Array[]) {
node *p, *pre, *head; // pre保存当前结点的前驱结点
head = new node;
head->next = NULL;
pre = head;
for(int i = 0; i < 5; i++) {
p = new node;
p->data = Array[i];
p->next = NULL;
pre->next = p;
pre = p;
}
return head;
}
int main() {
int Array[5] = {5, 3, 6, 1, 2};
node* L = create(Array);
L = L->next;
while(L != NULL) {
printf("%d", L->data);
L = L->next;
}
return 0;
}
2.查找元素
int search(node* head, int x) {
int count = 0;
node* p = head->next;
while(p != NULL) {
if(p->data == x) count++;
p = p->next;
}
return count;
}
3.插入元素
void insert(node* head, int pos, int x) {
node* p = head;
for(int i = 0; i < pos - 1; i++) p = p->next;
node* q = new node;
q->data = x;
q->next = p->next;
p->next = q;
}
4.删除元素
void del(node* head, int x) {
node* p = head->next;
node* pre = head;
while(p != NULL) {
if(p->data = x) {
pre->next = p->next;
delete(p);
p = pre->next;
}
else {
pre = p;
p = p->next;
}
}
}
四.静态链表
原理:建立一个结构体数组,令数组下标直接表示结点的地址;
struct Node {
typename data;
int next;
}node[size];
node[11111] = 22222;// 下标为此结构体地址,内容为下一个结构体的地址
node[22222] = 33333;
node[33333] = -1;
注意:不要把结构体类型名(Node)和结构体变量名(node)取成相同的名字,防止 sort 函数使用时出错;
五.题目
*通用步骤:
1.定义静态链表:
struct Node {
int address;
typename data;
int next;
XXX; //Node的一个性质,例如设置成 bool flag
}node[maxn];
2.对XXX进行初始化:
for(int i = 0; i < maxn; i++)
node[i].XXX = 0;
3.遍历链表——> 对XXX标记 + 计数:
int p = head, count = 0;
while(p != -1) {
XXX = 1;
count++;
p = node[p]->next;
}
4.定义静态链表时数组下标是address,会使得数组不连续,这步的目的在于把有效结点移动到最前面,下标变成 0~count,address 储存在 struct 中:
bool cmp(Node a, Node b) {
if(a.XXX == -1 || b.XXX == -1) return a.XXX > b.XXX;
else //第二级排序
}
1.PATA1032
思路:
先遍历第一个链表,标记每个结点(不要另开数组,直接在结构体里定义 flag);
注意:
1).不能通过判断 -1 出现的次数为二决定输出 -1,为什么?
2).scanf 输入%c 时可以使用空格,如果有空格的话则必须加上空格;
3).使用 %05d 输出地址,可以使不足五位的地址整数高位补零;
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100010;
int n;
struct Node {
char data;
int next;
bool flag;
}node[maxn];
int main() {
int head1, head2;
int count1 = 0;
scanf("%d%d%d", &head1, &head2, &n);
int adr, next_adr;
char x;
for(int i = 0; i < n; i++) {
scanf("%d %c %d", &adr, &x, &next_adr);
node[adr].data = x;
node[adr].next = next_adr;
node[adr].flag = false;
}
int p;
for(p = head1; p != -1; p = node[p].next)
node[p].flag = true;
for(p = head2; p != -1; p = node[p].next) {
if(node[p].flag == true) {
break;
}
}
if(p != -1) printf("%05d\n", p);
else printf("-1\n");
return 0;
}
2.PATA1052
思路:
直接舍弃链表这个结构——>不行,因为给出的 n 个 Node 可能不全为有效结点,需要用链表结构得到有效结点个数——> node 下标还是应该用 address,排序范围为 0~ maxn,不能下标用 0~n,排序范围为 0 ~n,否则在while(p != -1) { node[p].flag = true; count++; p = node[p].next; }
会很麻烦;
注意:
if(count == 0) printf("0 -1\n");
的情况;
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct Node {
int data;
int address;
int next;
bool flag;
}node[maxn];
bool cmp(Node a, Node b) {
if(a.flag == false || b.flag == false) return a.flag > b.flag;
else return a.data < b.data;
}
int main() {
int n, head;
scanf("%d%d", &n, &head);
int adr, next_adr, x;
for(int i = 0; i < n; i++) {
scanf("%d%d%d", &adr, &x, &next_adr);
node[adr].data = x;
node[adr].address = adr;
node[adr].next = next_adr;
}
for(int i = 0; i < maxn; i++) node[i].flag = false;
int p = head, count = 0;
while(p != -1) {
node[p].flag = true;
count++;
p = node[p].next;
}
if(count == 0) printf("0 -1\n");
else {
sort(node, node + maxn, cmp);
printf("%d %05d\n", count, node[0].address);
for(int i = 0; i < count; i++) {
if(i != count - 1)
printf("%05d %d %05d\n", node[i].address, node[i].data, node[i + 1].address);
else printf("%05d %d -1\n", node[i].address, node[i].data);
}
}
return 0;
}
3.PAT B1025
思路:
如何把链表按照顺序排列:不用另开数组,记录下每个 node 的顺序 order,用 sort 函数排序,得到 node[0~count];
注意:
这种倒来倒去的我就不太会,尤其是确定数组的下标;
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct Node {
int address;
int data;
int next;
int order;
bool flag;
}node[maxn];
bool cmp(Node a, Node b) {
if(a.flag == false || b.flag == false) return a.flag > b.flag;
else return a.order < b.order;
}
int main() {
for(int i = 0; i < maxn; i++) {
node[i].flag = false;
}
int head, n, k;
scanf("%d%d%d", &head, &n, &k);
int adr, x, next_adr;
for(int i = 0; i < n; i++) {
scanf("%d%d%d", &adr, &x, &next_adr);
node[adr].address = adr;
node[adr].data = x;
node[adr].next = next_adr;
}
int p = head, count = 0;
while(p != -1) {
node[p].order = count++;
node[p].flag = true;
p = node[p].next;
}
sort(node, node + maxn, cmp);
for(int i = 0; i < count; i = i + k) {
if(i + k - 1 < count) {
Node temp;
for(int j = i; j < (2 * i + k) / 2; j++) {
temp = node[j];
node[j] = node[2 * i + k - 1 - j];
node[2 * i + k - 1 - j] = temp;
}
for(int m = i; m < i + k - 1; m++) {
node[m].next = node[m + 1].address;
}
if(i != 0) node[i - 1].next = node[i].address;
if(i + k - 1 == count - 1) node[i + k - 1].next = -1;
if(count - (i + k) < k && count - (i + k) < k > 0) node[i + k - 1].next = node[i + k].address;
}
else break;
}
for(int i = 0; i < count; i++) {
if(i == count - 1) printf("%05d %d -1\n", node[count - 1].address, node[count - 1].data);
else printf("%05d %d %05d\n", node[i].address, node[i].data, node[i].next);
}
return 0;
}