一、顺序表那么好,为什么还需要链表
顺序表的缺点:
•
顺序
表
的除了表尾外其他位置插入
和删除元素需要移动其他元素,效率低
;平均时间复杂度为
O(n)
。
•
顺序表需要连续的空间,当数据量比较大时,如果没有足够的连续空间,顺序表失效
!!
为解决该缺点,设计了链表。
链表的插入和删除不需要移动其他元素,插入删除效率高;但失去了随机存取的优势
。
另:这是一种颠覆性创新,另起炉灶,不在顺序存储的框架内修修补补!!
单链表是最简单的链式结构,由此发散、引申,可以构造更复杂的结构,比如树、图。
基础不牢,地动山摇,必须把链表学好。
二、单链表的定义
•
单链表是通过在当前结点中存放后继结点的地址将序列数据像“链”一样连接起来,所以称为链表。
•
单链表的“单”指的是当前结点中只存放一个地址数据,即后继数据结点的地址。
三、单链表的结点形态
四、单链表的整体形态
在单链表中设立头结点,是为了方便在链表的头部插入和删除结点。
本节我们讲的单链表的头结点和数据结点是相同类型。
有时候为了方便,头结点中可以设一个指向链表头的指针、一个指向链表尾的指针和一个表示链表长度的整数。这时候头结点的类型就和链表结点的类型就不同了。
五、链表结点的C++描述
template<typename T>class list
{//单向链表
struct node
{
T data;
node* next;
};
node* head;//头结点指针
int length;//记录元素结点个数
}
这样,head就是头指针,length记录链表中元素结点的个数,不包括头结点。
六、生成一个结点
node* creatNode(T x)
{//为x生成结点,返回结点地址
node* t;
t = new node;
if (t == 0)
return 0;
t->data = x;
t->next = 0;
return t;
}
用new运算符,向操作系统申请node类型的空间。
若操作系统说,我也不富裕,拒绝了,则返回0,即空指针,否则返回新结点的地址。
七、初始化
开始时,生成一个头节点,在构造函数中实现。
void init()
{
head = new node;
head->next = 0;
length = 0;
}
list()
{
init();
}
head是一个变量,里面存的是地址,是头结点的地址,这个头结点没有名字。
八、在某个结点后面插入元素
void insert(node* p, node* q)
{//p是链表中某个结点的指针,q是一个新结点的指针
//将q插入到p的后面
q->next = p->next;
p->next = q;
length++;
}
void insert(node* p, T x)
{//在指针p所指的结点后面插入x
node* q, * t;
t = creatNode(x);
insert(p, t);
}
C++的函数重载,函数名相同,参数不同。
在结点p后面插入q结点,一定不能先断开p后面的指针,断开前,一定要有指针指向原来p后面的结点,
不然就会丢失结点。
九、在头结点后面插入元素
void push_front(T x)
{
insert(head, x);
}
有了前面 insert 函数的铺垫, 在头部插入结点的操作就是如此简单。
十、在链表尾部插入结点
void push_back(T x)
{
node* p;
p = head;
while (p->next)
{
p = p->next;
}
insert(p, x);
}
先找到链表的尾部结点,用指针p指向它,然后再p后面插入数据。
十一、得到第i个结点的地址
node* Address(int i)
{//得到第i个元素结点的地址
if (i<0 || i>length)
return 0;
if (i == 0)
return head;
if (i == 1)
return head->next;
int j = 0;
node* p = head;
while (j < i)
{
p = p->next;
j++;
}
return p;
}
在单链表中找到第 i 个结点,是一个基本操作。
十二、在第i个结点前插入数据
void insert(int i, T x)
{//在链表的第i个数据前面插入x
//即在第i-1个元素后面插入x
node* p;
if (i == 1)
push_front(x);
else if (i == length + 1)
push_back(x);
else
{
p = Address(i - 1);
insert(p, x);
}
}
要在第 i 个结点处插入数据,就是在第 i-1 个结点后插入数据,需要先找到第 i-1 个结点。
十三、删除第i个结点
void erase(int i)
{//删除第i个元素结点
if (i<1 || i>length)
return;
node* q, * t;
t = Address(i - 1);
q = t->next;
t->next = q->next;
delete q;
length--;
}
要删除第 i 个结点,需要先找到第 i-1 个结点。
十四、在链表中查找
node* find(T x)
{//查找元素x,若存在返回地址,否则返回空指针0
node* p = head->next;
while (p != 0)
{
if (p->data == x)
return p;
p = p->next;
}
return 0;
}
十五、释放单链表占用的空间
在析构函数中实现
~list()
{
node* p;
node* q;
while (head->next)
{
p = head->next;
head->next = p->next;
delete p;
}
delete head;
head = 0;
length = 0;
}
十六、完整代码
//#include "stdafx.h" //vc下面工程带的头文件,用dev编译时注释掉即可
#include <iostream>
using namespace std;
template<typename T>class list
{//单向链表
struct node
{
T data;
node* next;
};
node* head;//头结点
int length;
public:
void init()
{
head = new node;
head->next = 0;
length = 0;
}
list()
{
init();
}
int size()
{
return length;
}
void print()
{
node* p;
p = head->next;
while (p != 0)
{
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
node* creatNode(T x)
{//为x生成结点,返回结点地址
node* t;
t = new node;
t->data = x;
t->next = 0;
return t;
}
void insert(node* p, node* q)
{//p是链表中某个结点的指针,q是一个新结点的指针
//将q插入到p的后面
q->next = p->next;
p->next = q;
length++;
}
void insert(node* p, T x)
{//在指针p所指的结点后面插入x
node* q, * t;
t = creatNode(x);
insert(p, t);
}
void push_front(T x)
{
insert(head, x);
}
void push_back(T x)
{
node* p;
p = head;
while (p->next)
{
p = p->next;
}
insert(p, x);
}
node* Address(int i)
{//得到第i个元素结点的地址
if (i<0 || i>length)
return 0;
if (i == 0)
return head;
if (i == 1)
return head->next;
int j = 0;
node* p = head;
while (j < i)
{
p = p->next;
j++;
}
return p;
}
void insert(int i, T x)
{//在链表的第i个数据前面插入x
//即在第i-1个元素后面插入x
node* p;
if (i == 1)
push_front(x);
else if (i == length + 1)
push_back(x);
else
{
p = Address(i - 1);
insert(p, x);
}
}
void erase(int i)
{//删除第i个元素结点
if (i<1 || i>length)
return;
node* q, * t;
t = Address(i - 1);
q = t->next;
t->next = q->next;
delete q;
length--;
}
node* find(T x)
{
node* p = head->next;
while (p != 0)
{
if (p->data == x)
return p;
p = p->next;
}
return 0;
}
~list()
{
node* p;
node* q;
while (head->next)
{
p = head->next;
head->next = p->next;
delete p;
}
delete head;
head = 0;
length = 0;
}
};
int main()
{
list<int> L;
L.push_front(3);
L.push_front(2);
L.push_front(1);
L.print();
L.push_back(4);
L.push_back(5);
L.push_back(6);
L.print();
cout << L.size() << endl;
cout << (L.Address(6)->data) << endl;
L.insert(1, 44);
L.print();
L.insert(3, 55);
L.print();
L.insert(8, 66);
L.print();
cout << "begin to delete:\n";
L.erase(1);
L.print();
L.erase(L.size());
L.print();
L.erase(3);
L.print();
cout << L.find(55)->data << endl;
return 0;
}