《算法笔记》7.3 链表

一.链表的概念
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值