线性表依据存储结构的不同可以分为顺序表和链表。虽然顺序表可以通过动态分配内存空间的方式来避免溢出的可能性,但是在插入和删除时却需要大量的移动元素,对于链表而言,在存储时不需要使用连续的存储空间(和顺序表相比存储密度有所降低),插入和删除时不需要移动大量元素,只需要修改相应的指针。但是也失去了顺序表可随机存取的优点。
(关于顺序表的内容:顺序表)
单链表解决了顺序表需要大量连续的存储空间的缺点,但是由于单链表附加了指针域,也存在大量浪费存储空间的缺点。
通常情况下用头指针(头指针是指向链表第一个结点的指针)来标识一个单链表,如单链表L。为了操作上的方便,我们一般会在单链表的第一个结点之前加上一个结点,称为头结点(头结点一般不存放任何信息,但也可以记录表长等信息)。头结点的指针域指向单链表的第一个元素结点。
引入头结点的好处:
- 由于第一个数据结点的位置被存放在头结点的指针域中,因此链表的第一个位置上的操作和在表的其他位置的操作一致,无需进行特殊处理。
- 无论链表是否为空,其头指针都指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理得到了统一。
线性表中的链表除单链表外,还有常见的循环链表、双向链表、循环双向链表。它们的差异主要在于进行操作时依据自身的特点和条件修改不同的指针。包括单链表,均应该具体情况具体分析。
话不多说,上代码(本文所描述的是带头结点的单链表)。
linked_list.h:
#ifndef _LINK_
#define _LINK_
#include<iostream>
#pragma warning(diable:6011)
using namespace std;
typedef int Elemtype;
typedef struct LNode{
//单链表的结点的定义
Elemtype data;
struct LNode* next;
}*Linklist;
Linklist List_HeaderInsert(Linklist &L){
//头插法建立单链表,头结点中的信息用来存放该单链表中含有多少个结点(不包含头结点)
Linklist p; //用来构建新结点的工作结点
Elemtype x;
L = (Linklist)malloc(sizeof(LNode));
L->next = NULL; //创建头结点
L->data = 0;
cout << "请输入一个整数:" << endl;
cin >> x;
while (x != 1000)
{
L->data++;
//只要输入的值不是1000则单链表的建立就不会结束。
p = (Linklist)malloc(sizeof(LNode));
p->data = x;
p->next = L->next;
L->next = p;
cin >> x;
}
return L;
}
Linklist List_TailInsert(Linklist &L) {
//尾插法建立单链表,头结点中的信息用来存放该单链表中含有多少个结点(不包含头结点)
Linklist p; //用来构建新结点的工作指针
Elemtype x;
L = (Linklist)malloc(sizeof(LNode));
L->next = NULL; //创建头结点
L->data = 0;
LNode* t = L; //t表示尾指针,指向最后一个结点
cout << "请输入一个整数:" << endl;
cin >> x;
while (x != 1000)
{
L->data++;
p = (Linklist)malloc(sizeof(LNode));
p->data = x;
t->next = p;
t = p;
}
t->next = NULL; //尾指针的指针域置为空
return L;
}
void List_Insert(Linklist& L, int n) {
//在单链表中插入一个结点,n表示插入在第n个结点的后面,头结点表示第0个结点;
if (n > L->data || n < 0)
{
exit(0); //如果是插入在最后一个结点的后面的后面,则插入位置不合法;
}
LNode* p = L; //P当做工作指针,用来找到第N个结点
int i = 0;
while (i != n)
{
//找到待插入的结点,并让工作指针P指向它;
p = p->next;
i++;
}
cout << "请输入要插入的元素的值:" << endl;
int val;
cin >> val;
LNode* s = (Linklist)malloc(sizeof(LNode));
s->data = val;
s->next = p->next;
p->next = s;
}
void List_Print(Linklist L) {
//顺序输出单链表的结点的元素的值
while (L)
{
L = L->next; //因头结点存放的不是但链表的元素的值,所以将L的移动放在前边
if (!L)
{
//L为空时推出循环
break;
}
cout << L->data << '\t';
}
cout << endl;
}
void List_Delete(Linklist& L,Elemtype &e) {
//删除某一结点,并用e来接收该结点的值
cout << "请输入要删除结点的序号" << endl;
int n;
cin >> n;
if (n <= 0||n >L->data)
{
//要删除的结点的序号不合法
exit(0);
}
LNode* p=L;
int i = 0;
while (i != n-1)
{
//找到待删除的结点,并让工作指针P指向它;
p = p->next;
i++;
}
LNode* q = p->next;
p->next = q->next;
e = q->data;
free(q);
}
Linklist List_Reverse(Linklist& L) {
//将单链表就地逆置
LNode* p = L->next, * pre, * r = p->next;
p->next = NULL; //处理第一个结点
while (r!=NULL)
{
//只要P不是最后一个结点
pre = p;
p = r;
r = r->next;
p->next = pre;
}
L->next = p; //已经到了最后一个结点,让L指向它
return L;
}
#endif // !_LINK_
main函数:
#include"linked_list.h"
int main() {
Linklist L;
List_HeaderInsert(L);
List_Print(L);
List_Insert(L,4);
List_Print(L);
Elemtype a;
List_Delete(L, a);
cout << "已删除结点的值" << a << endl;
List_Print(L);
List_Reverse(L);
List_Print(L);
return 0;
}