CHAPTER_7 提高篇(1)——数据结构(1)
7.3.1链表的概念
线性表是一种很常用的数据结构,它是n个相同特性的元素组成的序列,分为顺序表和链表。其中顺序表我们可以简单地理解成“数组”的概念,它是一块连续的地址来存储给定长度的表结构。而链表则是由若干个结点组成(每个结点代表一个元素),它们在内存中并不是连续在一起存储的,因此每个结点内含有一个指针指向下一个元素。
我们可以用结构体表示链表:
struct node {
type date; //type表示元素的类型,例如int或者char
node* next;
};
每一个结点都有一个指针next,指向下一个结点的位置,这样就像串链子一样形成一个线性结构。需要注意的是,第一个结点通常不存放数据,我们将其称作头结点(head),它标志着一个链表的开始。同样地,最后一个结点的指针next的值为空,它标志着链表的结束。
7.3.2为链表结点分配内存空间
上一节我们已经了解了链表结点的定义,这节我们介绍如何在每次使用新结点时分配相应的内存 空间。关于内存的申请和释放,我们使用C++的new和delete关键字。
new运算符
new是用来申请动态空间的运算符,其返回类型是申请的同变量类型的指针,其基本用法如下:
typename* p=new typename;
例如申请一个int型变量和一个node型变量:
int* p=new int;
node* p=new node;
有一点要注意,申请空间有可能失败,当失败时会启动C++异常机制处理,在没有特殊处理的情况下会退出程序。不过,一般正常分配一个结点空间,是不会失败的。
delete运算符
delete运算符与new成对使用,当我们用指针p申请了一个对应的空间,在使用完毕后切记使用delete将空间释放,否则容易产生内存泄露。
node* p=new node;
delete(p); //释放内存
7.3.3链表的基本操作
链表的创建
我们已经能够创建零散的结点了,接下来要做的是把这些结点连接成链表。方法也很简单,用for循环来把每个结点的next指向下一个即可。例如下面存储int型数据的链表。
typedef struct Node {
int date;
node *next;
}node;
node* create(int array[],int n) { //array为需要加入链表的数据,n为数据个数
node *p, *pre, *head; //pre记录当前结点的前一个结点,head为头结点
head=new node;
head->next=NULL; //头结点的指针域初始化为空
pre=head; //前驱结点初始化为第一个结点
for(int i=0;i<n;i++) {
p=new node; //新建一个结点
p->data=array[i]; //赋值
p->next=NULL; //指针域设为空,在下面的步骤中将其放在链表最后面
pre->next=p; //将p连接之最后
pre=p; //pre记录最后一个结点的前一个
}
return head;
}
查找元素
我们如何在链表中查找是否有某元素x呢?其实也很简单,我们设立一个计数器count并将其初始化为0,然后从头结点开始遍历链表,依次判断当前结点的date是否等于x,如果相等则令count++。
int search(node* head,int x) {
int count=0;
node *p=head->next; //从第一个结点开始
while(p) {
if(p->data==x)
count++;
p=p->next;
}
return count;
}
插入元素
我们先明确链表插入的定义:我们在链表的第i个位置x,是指在不包括头结点的情况下,插入完成后使第i个元素成为x。例如1 2 3 4,我们要在第3个位置插入5,那么插入结果为1 2 5 3 4。
我们要在第i个位置插入结点p,要找到第i个位置的前一个结点pre,例如要在第1个位置插入要找到头结点,在第3个位置插入要找到第二个结点(头结点不计入)的位置。找到前一个结点pre后,用临时指针tmp存储pre->next,然后令pre->next=p,p->next=tmp,这样就完成了结点的插入。如下图
void insert(node* head,int pos,int x) {
node *p=new node, *pre=head, *tmp;
p->data=x;
for(int i=0;i<pos-1;i++) { //找到插入位置的前一个结点
pre=pre->next;
}
tmp=pre->next;
pre->next=p;
p->next=tmp;
}
删除元素
对链表来说,删除元素x是指删除链表上所有值为x的结点。删除结点与插入类似,需要一个pre指针来指向欲删除结点p的前一个结点,令pre->next=p->next,delete(p)即可。
void del(node* head, int x) {
node *p=head->next, *pre;
while(p) { //从头结点后一个开始遍历,pre始终指向p前一个结点
if(p->data==x) {
pre->next=p->next;
delete(p);
}
p=p->next;
pre=pre->next;
}
}
上面介绍了单链表的简单原理。而C++在STL中也有链表相关容器list,它是一个双向链表。使用方法可以参考博客 C++中list用法详解_Donny's Blog-优快云博客_c++list 。