线性表
线性表的类型定义
线性表是n个数据元素的有限序列,是最简单的数据结构,有以下几个特点。
- 同一线性表中元素必须属于同一数据对象 ;
- 相邻元素之间有序偶关系;
线性表的抽象数据类型如下1:
线性表的顺序表示
顺序表示是用一组地址连续的存储单元依次存储线性表的数据元素,有点类似于数组。(因为可以直接用数组替换所以不再细讲)
线性表的链式表示和实现
重点!
重点!
重点!
链式表示【或者直接称为链表】不要求存储的数据逻辑上相邻的物理位置也相邻【这句话有点拗口 ,其实就是在链表中前一个数据的位置不一定和下一个数据的存储位置相邻】
从大一C语言开始到现在写起来还感觉有点麻烦的东西(代码弱鸡实力无疑)。链表一共有三种表现形式:线性链表、双向链表和循环链表。虽然在C++中感觉可以有很多东西代替链表的实现,但是在操作系统实验里面用到链表的东西还是挺多的,所以在这里要重新实现一次。
线性链表
在线性链表中,每个数据元素ai除了要保存自身的数据信息外,还需要保存后继元素ai+1的存储位置。
给定的链表类定义如下:
//节点
class listnode
{
public:
listnode(int value)
{
val = value;
next = NULL;
}
int val;
listnode* next;
};
//链表类
class list
{
public:
//创建链表
list();
//删除链表
~list();
//插入链表
void Insertlist(int newvalue);
//删除节点
bool Deletelist(int data);
//测试输出
void Printlist();
//链表中的数据个数
int len_of_list;
private:
//头结点
listnode* head;
//尾节点
listnode* tail;
};
创建链表
//创建链表
list::list()
{
head = new listnode(0);
tail = head;
len_of_list = 0;
}
插入节点
void list::Insertlist(int newvalue)
{
listnode* newone = new listnode(newvalue);
tail->next = newone;
tail = newone;
head->val++;
}
删除节点
bool list::Deletelist(int data)
{
listnode* l1 = head, * l2 = l1;
while (l1 != NULL)
{
if (l1->val != data)
{
l2 = l1;
l1 = l1->next;
}
else
{
break;
}
}
if (l1 == NULL)
return false;
else if (l1 == head)
{
head = l1->next;
free(l1);
}
else
{
l2->next = l1->next;
l1->next = NULL;
free(l1);
}
return true;
}
打印输出
void list::Printlist()
{
listnode* l1 = head->next;
while (l1 != NULL)
{
cout << l1->val << " ";
l1 = l1->next;
}
cout << endl << endl;
}
双向链表
双向链表的实现和线性链表差不多,只需要在每个结点增加一个前继指针即可。
修改后的结点类定义如下
class listnode
{
public:
listnode(int value)
{
val = value;
next = NULL;
prev = NULL;
}
int val;
listnode* next;
listnode* prev;
};
创建链表
list::list()
{
head = new listnode(0);
tail = head;
len_of_list = 0;
}
插入节点
void list::Insertlist(int newvalue)
{
listnode* newone = new listnode(newvalue);
tail->next = newone;
newone->prev = tail;
tail = newone;
head->val++;
}
删除节点
bool list::Deletelist(int data)
{
listnode* l1 = head->next, * l2 = head;
while (l1 != NULL)
{
if (l1->val != data)
{
l2 = l1;
l1 = l1->next;
}
else
{
break;
}
}
if (l1 == NULL)
return false;
else
{
l2->next = l1->next;
if(l1->next!=NULL)
l1->next->prev = l2;
else
{
tail = head;
}
l1->next = NULL;
l1->prev = NULL;
free(l1);
}
return true;
}
打印输出
void list::Printlist()
{
listnode* l1 = head->next;
while (l1 != NULL)
{
cout << l1->val << " ";
l1 = l1->next;
}
cout << endl << endl;
l1 = tail;
while (l1!=head)
{
cout << l1->val<<" ";
l1 = l1->prev;
}
cout << endl << endl;
}
循环链表
循环链表相当于一个环(?)平时刷题感觉不经常遇到,但在操作系统互斥中消费者和生产者需要用到。循环链表和线性链表不同之处就在于尾指针始中指向头结点。
循环链表的结点和链表的类定义与线性链表一样,这里不再赘述。
创建链表
list::list()
{
head = new listnode(0);
tail = head;
len_of_list = 0;
}
插入节点
void list::Insertlist(int newvalue)
{
listnode* newone = new listnode(newvalue);
tail->next = newone;
tail = newone;
tail->next = head;
head->val++;
}
删除节点
bool list::Deletelist(int data)
{
listnode* l1 = head->next, * l2 = head;
while (l1 != head)
{
if (l1->val != data)
{
l2 = l1;
l1 = l1->next;
}
else
{
break;
}
}
if (l1 == head)
return false;
else
{
l2->next = l1->next;
//最后一个结点
if (l1 == tail)
{
tail = l2;
}
l1->next = NULL;
free(l1);
}
return true;
}
打印输出
void list::Printlist()
{
listnode* l1 = head->next;
while (l1 != head)
{
cout << l1->val << " ";
l1 = l1->next;
}
cout << endl << endl;
}
线性表常见题型
线性表常见的提醒包括了合并链表、链表排序等等。这些题目会在接下来的力扣刷题中逐一进行求解。
最后的碎碎念
大一学C语言感觉最难的一章就是指针和文件,然而没想到到了大二数据结构中最简单的一种就是链表【然后我也没想到数据结构里感觉最难的排序和查找居然是大三算法中最简单的递归分治】。总体来说线性表的实现难点在于指针的正确应用,包括删除时如何确保不会有野指针的产生等等。虽然在一些情况下野指针不会产生问题,但是在某些特殊环境下野指针会造成错误【比如操作系统实验中奇奇怪怪的bad alloc错误】.
[图源自旭玲姐姐的PPT] ↩︎