(优缺点文字部分转自: http://blog.csdn.net/zhang_minli/article/details/50489996)
顺序表和链表de优缺点
顺序表的优点
方法简单,各种高级语言中都有数组,容易实现。
不用为表示结点间的逻辑关系而增加额外的存储开销(链表要增加额外的指针域)。
顺序表具有按元素序号随机访问的特点。
缺点
在顺序表中做插入删除操作时,平均移动大约表中一般的元素,因此对n较大的顺序表效率低。
需要预先分配足够大的存储空间,估计过大,可能会导致顺序表后部大量闲置;预先分配过小,又会造成溢出。
链表的优缺点恰好和顺序表相反。
链表和顺序表的插入、删除操作时间复杂度O(n)是两个不同的概念,链表O(n)主要是花费在查找上,顺序表O(n)主要花费在移动元素上,两者没有可比性。
在实际中应怎样选择存储结构?
1. 基于存储的考虑
顺序表在程序执行之前必须明确规定它的存储规模,也就是说事先对“MAXSIZE”要有合适的设定,过大造成浪费,过小造成溢出。可见对线性表的长度或存储规模难以估计时,不宜采用顺序表;
链表不用事先估计存储规模但链表的存储密度较低,顺序存储结构存储密度是1,链式存储结构的存储密度是小于1的。
2. 基于运算的考虑
在顺序表中按序号访问ai的时间性能是O(1),而链表中按序号访问的时间性能是O(n),所以如果经常做的运算是按序号访问数据元素,显然顺序表优于链表;而在循序表中做插入、删除操作时平均移动表中一半的元素,当数据元素的信息量较大且表较长时,这一点是不应忽视的;在链表中做插入、删除,虽然也要找插入位置,但操作主要是比较操作,从这个角度考虑显然链表优于顺序表。
3. 基于环境的考虑
顺序表容易实现,任何高级语言中都有数组类型,链表的操作是基于指针的,相对来讲前者简单些。
总之,顺序表和链表没有绝对的优劣之分,两种存储结构各有长短,关键看你看重什么,也就是要依据实际问题中的主要因素而定。通常“较稳定”的线性表选择顺序存储,而频繁做插入删除操作的,即动态性较强的线性表适合用链式存储。
下面是对顺序表、单链表的初始化、插入、寻找、删除、销毁、输出等的比较。
C版:
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
#define Overflow 2
#define Underflow 3
#define NotPresent 4
#define Duplicate 5
typedef int Status;
typedef int ElemType;
typedef struct Node {
ElemType val;
Node* link;
}Node;
typedef struct {
int maxLength;
int n;
ElemType* val;
}SeqList;
typedef struct {
Node* first;
int n;
}SingleList;
typedef struct {
Node* head;
int n;
}HeaderList;
/*----------------Init()----------------*/
Status Init(SeqList* L, int mSize) {
L->maxLength = mSize;
L->n = 0;
L->val = (ElemType*)malloc(sizeof(ElemType)*mSize);//生成一维数组空间
if (!L->val) return ERROR;
return OK;
}
Status Init(SingleList* singleList) {
if (!singleList) return ERROR;
singleList->first = NULL;
singleList->n = 0;
return OK;
}
Status Init(HeaderList* h)//因为要附加表头结点,故需为其开辟空间
{
h->head = (Node*)malloc(sizeof(Node));
if (!h->head) return ERROR;//有可能申请空间失败
h->head->link = NULL;
h->n = 0;
return OK;
}
Status Init(Node* node) {
if (!node) return ERROR;
node->link = NULL;
return OK;
}
/*----------------Find()----------------*/
Status Find(HeaderList h, int i, ElemType* x)
{
Node* p;
int j;
if (i < 0 || i > h.n - 1) return ERROR;
p = h.head;
for (j = 0; j <= i; j++) {
p = p->link;
}
*x = p->val;
return OK;
}
Status Find(SingleList L, int i, ElemType* x) {
if (i<0 || i>L.n - 1) return ERROR;
Node* p;
p = L.first;//p指向下标0处
for (int j = 0; j < i; j++) p = p->link;//p走了i步--》两句一共走到了下标i处
*x = p->val;
return OK;
}
Status Find(SeqList L, int i, ElemType* x) {
if (i<0 || i>L.n - 1) return ERROR;
*x = L.val[i];
return OK;
}
/*----------------Insert()----------------*/
Status Insert(HeaderList* h, int i, ElemType x)
{
Node *pCur, *pNext;
int j;
if (i < -1 || i > h->n - 1) return ERROR;//注意:是i < -1 ,虽然已定义表头结点,但是这与下标i的取值无关!i照样可以取-1,表示插在最前面!
pCur = h->head;
for (j = 0; j <= i; j++) {//“=”勿忘!!!
//若传入的i为-1(插入表首元素位置),则该循环不执行。pCur指向表头结点,将pNext->link与pCur->link(原首个元素)相连。
//注意:i为0时(插入表的第二个元素位置),需要将pCur后移一个,使pCur指向原首个元素。以便后面将pNext连接到pCur后面(原第二个元素)。
pCur = pCur->link;
}
//pNext = (Node*)malloc(sizeof(Node));
pNext = new Node;
pNext->val = x;
pNext->link = pCur->link;
pCur->link = pNext;
h->n++;
return OK;
}
Status Insert(SingleList* L, int i, ElemType x) {//多半将pNext插入到pCur之后,除非i=-1,插到最前面。
Node* pCur, *pNext;
if (i<-1 || i>L->n - 1) return ERROR;
pCur = L->first;
for (int j = 0; j < i; j++) {
pCur = pCur->link;
}
//创建要插入的结点(申请内存空间)
pNext = (Node*)malloc(sizeof(Node));//复制x值到新Node,需要先为其开辟空间。
pNext->val = x;
//插入链表
if (i > -1) {
pNext->link = pCur;
pCur->link = pNext;
}
else {// i = -1
pNext->link = L->first;
L->first = pNext;
}
L->n++;
return OK;
}
Status Insert(SeqList* L, int i,ElemType x) {
if (i<0 || i > L->n - 1) return ERROR;
if (L->n == L->maxLength) return ERROR;//顺序表独有!
for (int j = L->n - 1; j > i; j--) {
L->val[j + 1] = L->val[j];
}
L->val[i + 1] = x;
L->n++;
return OK;
}
/*----------------Delete()----------------*/
Status Delete(HeaderList* h, int i)
{
Node *pCur, *pPre;//pCur:被删者,pPre:被删者的前驱结点
if (!h->n /*|| !h->head*/) return ERROR;
if (i < 0 || i > h->n - 1) return ERROR;
pPre = h->head;
for (int j = 0; j < i; j++) {//注意:此处不加"="
//若i=0,即删除首元素(pCur),无需将pPre后移(此时pPre指向表头):直接将pCur与pPre->link(首个元素)相连。释放pCur。
//若i=1,即删除第二个元素,需要将pPre后移一位,指向第一个元素,然后将pCur指向pPre->link(第二个元素)。释放pCur。
pPre = pPre->link;
}
pCur = pPre->link;
pPre->link = pCur->link;
free(pCur);
h->n--;
return OK;
}
Status Delete(SingleList* L, int i)
{
Node *pCur, *pPre;//pCur:被删者,pPre:被删者的前驱结点
if (!L->n /*|| !h->head*/) return ERROR;
if (i < 0 || i > L->n - 1) return ERROR;
pCur = L->first;
pPre = L->first;//pPre走到了下标0处
for (int j = 0; j < i - 1; j++) {//i为0或1时不移动。否则,将pPre移动i-1次,两句j一共移动到下标i-2(+1)处,即i-1处
//object:找到被删者i的前驱结点√
pPre = pPre->link;
}
if (i == 0) L->first = L->first->link;
else {
pCur = pPre->link;//通过其前驱节点pPre,将被删者pCur指向更新,以便后面删除pCur
pPre->link = pCur->link;
}
free(pCur);
L->n--;
return OK;
}
Status Delete(SeqList* L, int i) {
int j;
if (i<0 || i>L->n - 1) return ERROR;
if (!L->n) return ERROR;
for (j = i + 1; j <= L->n - 1; j++) {
L->val[j - 1] = L->val[j];
}
L->n--;
return OK;
}
/*----------------Output()----------------*/
Status OutPut(HeaderList h)
{
Node *pCur;
if (!h.n) return ERROR;
Version 1
//pCur = h.head;//注意此处若加了link,则需先打印,再后移pCur
//while (pCur->link)
//{
// pCur = pCur->link;//①保证了头结点的val不打印(因为无意义)
// //②(遍历到倒数第二个元素时)保证了循环体内可以打印出最后一个元素
// printf("%d ", pCur->val);
//}
//Version 2 & 3
pCur = h.head->link;//保证了头结点的val不打印(因为无意义)。注意此处加了link,需先打印元素,再后移pCur
while (pCur)//循环条件若为pCur->link,则循环结束后,最后一个元素还需打印
{
printf("%d ", pCur->val);
pCur = pCur->link;
}
//printf("%d ", pCur->val);//循环条件若为pCur->link,为打印出最后一个元素,需要在循环体外加上此句。
printf("\n");
return OK;
}
Status Output(SingleList L) {
if (!L.n) return ERROR;
Node* p = L.first;
while (p)
{
printf("%d ", p->val);//此处可看出 first不是表头结点,仅仅是SingleList的头结点。无dummy作用。
p = p->link;
}
return OK;
}
Status Output(SeqList L) {
if (!L.n) return ERROR;
for (int i = 0; i <= L.n - 1; i++) {
printf("%d ", L.val[i]);
}
return OK;
}
Status OutPut(Node* node)
{
Node* p;
if (/*!L.val ||*/ !node->link) return ERROR;
p = node/*->link*/;
while (p->link)//若不判断p->link,则最后一个p无后继结点,打印最后一个p的元素后,p继续后移,之后本应退出循环却未退出,导致结尾多打印出了一个’?’
{
printf("%d ", p->val);
p = p->link;
}
printf("\n");
return OK;
}
Status OutputNode(Node* node)
{
Node* p;
// if (/*!L.val ||*/ !node->val) return ERROR;
p = node;
while (p)//若不判断p->link,则最后一个p无后继结点,打印最后一个p的元素后,p继续后移,之后本应退出循环却未退出,导致结尾多打印出了一个’?’
{
printf("%d ", p->val);
p = p->link;
}
printf("\n");
return OK;
}
/*----------------Destory()----------------*/
void Destroy(HeaderList* h)
{
Node* temp;
Node* head = h->head;
(*h).n = 0;
while (head) {
temp = head->link;
free(head);
head = temp;
}
}
void Destroy(SingleList* L)
{
Node* temp;
Node* head = L->first;
L->n = 0;
while (head) {
temp = head->link;
free(head);
head = temp;
}
}
void Destroy(Node* node)
{
Node* temp;
while (node) {
temp = node->link;
free(node);
node = temp;
}
}