数据的存储结构分为链式存储结构,线性存储结构。不管什么类型的数据结构,都会以这两种存储方式在计算机中存储。
线性存储结构就是开辟一段连续的内存(内存大小已经确定),将数据存储在这段连续内存中,这种存储结构的优点是可以快速地取出元素,时间复杂度为O(1)缺点是插入和删除需要移动大量的元素,时间复杂度为O(n)
链式存储结构是通过一小块一小块内存连接起来而构成链表。我们开辟一小段内存,在内存中存储数据和指向下一块内存的指针,将这些内存连接起来。链表的插入和删除很方便时间复杂度为O(1)但是链表的读取就要从头开始,时间复杂度为O(n)
因此这两种存储方式各有利弊。
下面我们来说一下链表
例如我们声明一个结构体:
struct Node
{
ElemType data;
struct Node* next;
}*pNode;
这样我们就可以使用new为这段内存开辟一小段空间
pNode=new Node;
链表又分为单链表,双向链表,循环链表。
双向链表还有一个前驱指针,
typedef struct DulNode
{
ElemType data;
struct DulNode *prior;
struct DulNode *next;
}DulNode,*DuLinkList;
循环链表的有一个指针指向尾节点,尾节点的指针指向头节点。
链表的插入分为头插法和尾插法,用头插法构建链表每次都是在头节点的后面插入节点,尾插法就是每次在新节点的后面插入节点,这两种插法构建出来的链表不一样需要注意。
假如我将’a’,’b’,’c’,’d’,’e’,’f’这几个字符存储在链表中来看一下头插法和尾插法的不同。
//头插法
#include<iostream>
using namespace std;
typedef struct Node
{
char c;
struct Node *next;
}*pNode;
int main()
{
pNode head=new Node;
head->next=nullptr;
char a;
cin>>a;
while(a!='q')
{
pNode p=new Node;
p->c=a;
p->next=head->next;
head->next=p;
cin>>a;
}
pNode q;
q=head->next;
while(q!=nullptr)
{
cout<<q->c<<endl;
q=q->next;
}
return 0;
}
我们看到使用头插法输出数据会相反。
//尾插法
#include<iostream>
using namespace std;
typedef struct Node
{
char c;
struct Node *next;
}*pNode;
int main()
{
pNode head=new Node;
head->next=nullptr;
char a;
cin>>a;
pNode q;
q=head;
while(a!='q')
{
pNode p=new Node;
p->c=a;
p->next=nullptr;
q->next=p;
q=q->next;
cin>>a;
}
pNode m;
m=head->next;
while(m!=nullptr)
{
cout<<m->c<<endl;
m=m->next;
}
}
单链表的删除
#include<iostream>
using namespace std;
typedef struct Node
{
char c;
struct Node *next;
}*pNode;
void Destroy(pNode,char);
int main()
{
pNode head=new Node;
head->next=nullptr;
char a;
cin>>a;
pNode q;
q=head;
while(a!='q')
{
pNode p=new Node;
p->c=a;
p->next=nullptr;
q->next=p;
q=q->next;
cin>>a;
}
char a_1='c';
Destroy(head,a_1);
pNode m;
m=head->next;
while(m!=nullptr)
{
cout<<m->c<<endl;
m=m->next;
}
}
void Destroy(pNode h,char a)
{
pNode p,q;
p=h->next;
q=h;
while(p->c!=a)
{
p=p->next;
q=q->next;
}
q->next=p->next;
delete p;
}