一窥--顺序表和单链表
顺序表和单链表
真正意义上自己弄出来的,发篇博客记录一下
顺序表
类似于数组,元素都是相邻的,这也决定了它比较容易和比较适合查询。但缺点就是长度有限。
时间复杂度
- 查询操作 O(1)
- 插入和删除操作 O(n)
代码实现
#include<iostream>
#include<string>
using namespace std;
const int MAXSIZE = 20; //线性表最大长度
typedef int ElemType;
typedef struct{
ElemType data[MAXSIZE];
int length; //线性表长度
} Sqlist;
//按照1,2,...,n进行计数
void SetElem(Sqlist &L){
for (int i=0;i<L.length;i++)
L.data[i] = i+1;
}
int GetElem(Sqlist L,int i){
if(L.length == 0 || i<1 || i>L.length)
return -1;
return L.data[i-1];
}
string ListInsert(Sqlist *L,int index,ElemType num){
if( L->length == MAXSIZE)
return "List is already full";
if(index<1 || index>=L->length+1)
return "Index is out of range";
if(index<=L->length){
int k;
//元素后移
for (k=L->length-1;k>=index-1;k--){
//error这样长度不变:L->data[k] = L->data[k-1];
L->data[k+1] = L->data[k];
}
L->data[index-1] = num;
L->length++;
return "Inserted!";
}
}
string ListDelete(Sqlist *L,int index){
if (L->length==0)
return "List is empty";
if (index<1 || index >L->length)
return "Index is out of range";
if (index<=L->length){
int k;
for (k=index-1;k<L->length-1;k++)
L->data[k] = L->data[k+1];
L->length--;
return "Deleted!";
}
}
int main(){
Sqlist sq;
sq.length = 10;
SetElem(sq);
cout<<ListInsert(&sq,10,100)<<endl;
for (int i=0;i<sq.length;i++)
cout<<GetElem(sq,i+1)<<" ";
cout<< endl;
cout<<ListDelete(&sq,1)<<endl;
for (int i=0;i<sq.length;i++)
cout<<GetElem(sq,i+1)<<" ";
cout<< endl;
return 0;
}
缺点
- 长度有限,不灵活
- 查询和删除操作太慢
- 会有多余的空间被浪费 :
MAXSIZE-length
- 当改变长度时候,内存可能需要进行大面积的数值迁移,浪费内存资源
单链表
长度随心,非常灵活,创建麻烦些,但是一劳永逸
组成:结点
- 数据域:存储数据元素信息的域
- 指针域:存储直接后继位置的域
- 头指针:标志作用,无论链表是否为空,它不为空。且位于第一个元素之前,它的数据域一般无意义(可以用来存放链表长度)。
时间复杂度
- 查询操作:O(n)
- 插入/删除操作:O(1)
代码实现
#include<iostream>
#include<string>
using namespace std;
typedef int ElemType;
struct Node{
ElemType data;//数据域 node->data
struct Node *next;//指针域 node->next
};
string CreateListHead(Node *L){//L是现在是空表的头指针
L->next = NULL;//头结点初始化
Node *p = new Node;
if(!p)
return "Create fail";
cin>>p->data;
while(p->data){//当输入为0,停止创建
p->next = L->next;
L->next = p;
p = new Node;
cin>>p->data;
}
delete p;
return "Create list from head!";
}
string CreateListTail(Node *L){
L->next = NULL;
Node *p = new Node;
Node *t = L;//指向尾结点
if(!p)
return "Create fail (from tail)";
cin>>p->data;
while(p->data){
t->next = p;
t = p;
p = new Node;
cin>>p->data;
}
t->next = NULL;//最后指向NULL
delete p;
return "Create list from tail!";
}
int GetElem(Node *L,int index){
int j=1;
Node *p = new Node;
p = L->next;//指向头结点
while(p && j<index){
p = p->next;
j++;
}
//p&&j<i 条件取反
if (!p){
return -1;
}
return p->data;
}
string ListInsert(Node *L,int index,ElemType e){
int j;
Node *p,*s;
p = new Node;
s = new Node;
if(!p && !s)
return "Insert fail";
p = L; //注意插入操作,体现头结点的好处:统一处理的代码
j = 1;
while(p && j<index){
p = p->next;
j++;
}
if (!p)
return "Index out of change";
s->data = e;
s->next = p->next;
p->next = s;
//这里p和s不能删除
return "Inserted!";
}
string ListDelete(Node *L,int index,ElemType e){
int j;
Node *p,*s= new Node;
p = L;//p指向头
j = 1;
while(p->next && j<index){ //p->next 是要删除的,这样,p就保存了删除的结点前一个结点的信息
p = p->next;
++j;
}
if(!(p->next)){
return "Index out of change";
}
s = p->next;
p->next = s->next;
delete s; //删除结点
return "Deleted!";
}
void ShowList(Node *L){
Node *p = L->next;
if(!p)
cout<<"List is empty"<<endl;
while(p){
cout<<p->data<<" ";
p = p->next;
}
cout<<endl;
}
int main(){
Node *node = new Node;
CreateListHead(node); //头插法,倒着插入 ,1,2,3
ShowList(node);
cout<<ListInsert(node,2,-10)<<endl;
ShowList(node);
Node *_node = new Node;
CreateListTail(_node);
ShowList(_node);
cout<<ListInsert(_node,2,-10)<<endl;
ShowList(_node);
delete node;
delete _node;
return 0;
}
缺点
- 查询消耗时间
- 在中间位置插入时候,时间复杂度大