题目:
PAT-A 1074 Reversing Linked List
PAT-A 1052 Linked List Sorting
PAT-A 1097 Deduplication on a Linked List
1. 链表的定义
struct node{
typename data; //数据域
node* next; //指针域
};
2. 使用malloc函数或new运算符为链表节点分配空间
2.1 malloc函数
malloc函数是C语言中stdlib.h头文件下用于申请动态内存的函数,其返回类型是申请的同变量类型的指针:
typename* p = (typename*)malloc(sizeof(typename));
int* p1 = (int*)malloc(sizeof(int));
node* p2 = (node*)malloc(sizeof(node));
malloc申请一块大小为sizeof(node)空间,并且返回指向这块空间的指针,但是此时这个指针是一个未确定类型的指针void*,因此需要把他强转为node*型,然后赋给p2
2.2 new运算符
new是C++中用来申请动态空间的运算符,其返回类型同样是申请的同变量类型的指针
typename* p = new typename;
int* p1 = new int;
node* p2 = new node;
如果申请失败,则会启动C++异常机制处理而不是返回空指针NULL
2.3 内存泄漏
free()函数是对应的malloc函数的 free(p);
delete()函数是对应new运算符的 delete(p);
C/C++设计者认为程序员完全有能力自己控制内存的分配和释放,因此把对内存的控制操作全部交给了程序员。切记在使用完malloc和new开辟出来的空间后必须将其释放,否则会造成内存泄漏
3. 链表的基本操作
3.1 创建链表
#include <iostream>
using namespace std;
struct node{
int data; //数据域
node* next; //指针域
};
//创建链表(关键函数)
node* create(int Array[]){
node *p,*pre,*head; //pre保存当前节点的前驱节点,head为头结点
head = new node; //创建头结点
head->next = NULL; //头结点不需要数据域,指针域初始为NULL
pre = head; //记录pre为head
for(int i=0;i<5;i++){
p = new node; //新建节点
//将Array[i]赋给新建的结点作为数据域,也可以scanf输入
p->data = Array[i];
p->next = NULL; //新结点的指针域设为NULL
pre->next = p; //前驱结点的指针域设为当前新建节点的地址
pre = p; //把pre设为p,作为下个节点的前驱结点
}
return head;
}
int main() {
int Array[5] = {5,3,6,1,2};
node* L = create(Array); //新建链表,返回的头指针head赋给L
L = L->next; //从第一个结点开始有数据域
while(L != NULL){
printf("%d ",L->data);
L=L->next;
}
return 0;
}
3.2 查找元素
int search(node* head,int x){
//在以head为头结点的链表上计数元素x的个数
int count = 0;
node* p = head->next;
while(p!=NULL){
if(p->data == x){
count++;
}
p = p->next;
}
return count;
}
3.3 插入元素
void insert(node* head,int pos,int x){
node* p = head;
for(int i=0;i<pos-1;i++){
p = p->next; //pos-1是为了到插入位置的前一个结点
}
node* q = new node; //新建结点
q->data = x; //新结点的数据域为x
q->next = p->next; //新节点的下一个结点指向原先插入位置的结点
p->next = q; //前一个位置的结点指向新节点
}
3.4 删除元素
//删除以head为头结点的链表中所有数据域为x的结点
void del(node* head,int x){
node* p = head->next; //p从第一个结点开始枚举
node* pre = head; //pre始终保存p的前驱结点指针
while(p != NULL){
if(p->data == x){ //数据域恰好为x,说明要删除该结点
pre->next = p->next;
delete(p);
p = p->next;
}else{ //数据域不是x,把pre和p都后移一位
pre = p;
p=p->next;
}
}
}
4. 静态链表
前面讲解的都是动态链表,即需要指针简历结点之间的连接关系。而对有些问题来说,结点的地址是比较小的整数,这样就没有必要去简历动态链表,而应使用方便的多的静态链表
静态链表的实现原理是Hash,即通过建立一个结构体数组,并令数组的下标直接表示结点的地址,来达到直接访问数组中的元素就能访问结点的效果。另外,由于结点的访问非常方便,因此静态链表是不需要头结点的。
//静态链表
struct Node{
typename data;
int next;
}node[1000];
next是一个int型的整数,用以存放下一个结点的地址,事实上就是数组下标。注意,在使用静态链表时,尽量不要把结构体类型名和结构体变量名取成相同的名字,因为由于静态链表是由数组实现的,那么就有可能需要对其进行排序,如果两个名字相同,sort函数就会报编译出错