您的位置:首页 > 理论基础 > 数据结构算法

数据结构 线性表之链表

2014-06-05 21:40 375 查看
1.链表:线性表的链式存储结构特点是用一组任意的存储单元存储线性表的数据元素。链表要包括头指针链表长度。结点要包含数据域和指针域

typedef struct Node //定义节点类型
{
ElemType data;
struct Node *next;
}Node;

typedef struct  //定义链表类型
{
Node *head;
Node *rear;
int length;
}LinkList;


2.链表的插入:解释了需要头结点的原因

在没有表头节点的时候,插入会出错。比如,当链表是空的时候。list->head ==NULL,不进入循环但进行p->next(对一个NULL取成员)就出错。如果插入位置大于表长度+1,就是在表尾之外,也会发生p为空的情况。

void InsertNode(LinkList *list, Node *new_node, int location)
{
Node *p=list->head;
int counter=1;
while( counter<location-1 && p!=NULL)
{
counter++;
p = p->next;
}
new_node->next = p->next;
p->next = new_node;
list->length++;
}


为了解决后一个问题,可以换用p->next!=NULL作为循环条件,就能发现这时p已经指向了表尾,停止循环。而第一个问题,可以引入一个表头节点来解决。

头指针和头结点的差别:头指针是list当中指向头结点的指针,而头结点数据域不包含有意义的数据元素,指针域指向第一个数据元素。若为空表,头结点的指针指向NULL.

void InsertNode(LinkList *list, Node *new_node, int location)
{
Node *p=list->head;
int counter=0;//有表头节点
while( counter<location-1 && p->next!=NULL)
{
counter++;
p = p->next;
}
new_node->next = p->next;
p->next = new_node;
list->length++;
}


3.链表的删除:注意把要删除的结点赋给一个指针,然后要把这个指针指向的内存释放

4.链表的创建:malloc返回的是一个指针,指向一个内存,其大小为Node类型的大小。(Node *)表示这个指针的类型是Node型指针,即指向Node型数据的指针。这里定义了new_node指针变量来指向这个刚申请的内存。

Node *new_node = (Node *)malloc(sizeof(Node));


5.链表的清除:要弄清有没有头结点,最后要保证头结点的指针域是NULL。
6.单链表结构和顺序存储结构 优缺点:

存储分配方式:顺序存储用一段连续的存储单元一次存储线性表的数据元素。单链表用任意的存储单元。
时间性能:顺序查找O(1),顺序插入删除O(n)。链表查找O(n),链表插入删除O(1)。
空间性能:顺序存储需要预分配存储空间,单链表可以动态分配。

7.静态链表:用数组描述的链表,数组的每个小标都对应一个data 和一个cur。这里相当于定义的是一个大小为MAXSIZE的数组,每个数组元素包含两个数据项。

#define MAXSIZE 1000
typedef struct
{
ElemType data;
int cur;
}StaticLinkList[MAXSIZE];


(1)初始化静态链表:即第一个数组的cur用来存放备用链表的第一个备用节点的下标(就是用来放还没有用的位置的下标),最后一个cur用来存放数组的第一个有值的元素的下标(只有空表时存的是0)。

Status InitList(StaticLinkList L)
{
for(int i=0; i<MAXSIZE-1;i++)
L[i].cur = i+1;
L[MAXSIZE-1].cur = 0;
return OK;
}


(2)申请内存空间:L[0].cur永远用来存放备用空间的第一个元素的位置。

int Malloc_SLL(StaticLinkList L)
{
int i = L[0].cur;
if(L[0].cur)  L[0].cur = L[i].cur;
return i;
}


(3)插入:其实跟单链表的操作是一样的,就是在备用链表里面找到空间j,让j的cur指向原来的第i个位置,然后让插入位置i之前的元素cur指向这个分配的内存。还有个重点是怎么找到第i个元素,要从MAXSIZE-1位置(备用链表的最后一个cur找到第一个位置,然后依次找到第i个)。

Status Insert(StaticLinkList L, int i, ElemType e)
{
int j,k,l;
k = MAXSIZE-1;
if( i<1 || j>ListLength(L)+1) return ERROR;
j = Malloc_SLL(L);
if(j)
{
L[j].data = e;
for( l = 1; l<=i-1; l++)
k = L[k].cur;
L[j].cur = L[k].cur;
L[k].cur = j;
return OK;
}
return ERROR;
}
(4)删除:同样需要释放j的位置,把这个位置添加到备用链表当中。

Status ListDelet(StaticLinkList L,int i)
{
int j,k = MAXSIZE-1;
for(j=1;j<i;j++)   //k是第i-1个元素的位置
k = L[k].cur;
j = L[k].cur;//j是第i个元素的位置
L[k].cur = L[j].cur;
Free_SSL(L,j);
return OK;
}


void Free_SSL(StaticLinkList L, int k)
{
L[k].cur = L[0].cur;
L[0].cur = k;//删除的这个位置就是备用链表的首个位置
}


(5)优缺点:插入和删除是只需要修改游标的值,不需要移动。缺点是没有解决连续存储表长难以确定的问题,失去了顺序存储结构随机存取的特性。
8.循环链表:循环链表用尾指针可以使查找开头和终端节点时操作位O(1)。
两个循环表合并,A在前,B在后。

List* merge(List * la, List *lb)
{ Node * p,*q;  p=la->rear->next;//p指向A表头结点
q=lb->rear->next;//q指向B表头结点
la->rear->next =lb->rear->next->next;		 // A表尾结点的指针域放B表链首结点地址
lb->rear->next=p;		// B表尾结点的指针域放A表头结点地址
free(lb);	//释放B表
return(la);}


9.双向链表:每个节点包含两个指针。一个前驱指针prior,一个后继指针next。


typedef struct DLNode{
struct DLNode * prior;
struct DLNode * next;
ElemType data;
}DLNode ;


typedef struct DLNode{
DLNode *head;
DLNode *rear;
int length;
}DLNode;




删除:使用一个指向第i-1个节点的指针p。

p->next->next->prior = p;
p->next = p->next->next;


插入:在第i个节点之前插入,仅使用一个指向第i个节点的指针p。插入的顺序,要先用p,最后改变p。

anew->next = p;
p->prior->next = anew;
anew->prior = p->prior;
p->prior = anew;


一个完整的链表程序。

#include <iostream>
#include <malloc.h>

using namespace std;

#define MAXSIZE 20
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int ElemType;
typedef int Status;

/************定义***************/
typedef struct Node //定义节点类型 { ElemType data; struct Node *next; }Node; typedef struct //定义链表类型 { Node *head; Node *rear; int length; }LinkList;

/**********函数***************/
LinkList *IniList()
{
LinkList *p = (LinkList *)malloc(sizeof(LinkList));
p->head = p->rear = (Node *)malloc(sizeof(Node));
p->head->next=NULL;
p->length = 0;
return p;
}

Node *GetNode(LinkList *list, int i)//list是指向LinkList结构体的指针
{
Node *p=list->head;
int counter=0;
while(counter<i && p->next!=NULL)//p!=NULL 即counter<list->length
{
counter++;
p = p->next;
}
if(p==NULL) return NULL;
return p;//返回指向第i个Node的指针
}

void InsertNode(LinkList *list, int location, ElemType x)
{
Node *p=list->head;
int counter=0;//有表头节点
while( counter<location-1 && p->next!=NULL)
{
counter++;
p = p->next;
}
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->data = x;
new_node->next = p->next;
p->next = new_node;
list->length++;
}

void CreatList(LinkList *list, ElemType x)//有表头节点时创建链表,新插入的节点位于链表开头
{
Node *new_node = (Node *)malloc(sizeof(Node));
new_node->data = x;
new_node->next = list->head->next;
list->head->next = new_node;
list->length++;
}

void DeletNode(LinkList *list, int location)
{
int counter = 0;
Node *temp,*p = list->head;
while(counter<location-1 && p->next!=NULL)
{
counter++;
p = p->next;
}
temp = p->next;
p->next = p->next->next;
free(temp);
list->length--;
}

void ClearList(LinkList *list)
{
Node *q,*p = list->head;
p = p->next;
while(p!=NULL)
{
q = p->next;
free(p);
p = q;
}
list->head->next = NULL;
}

void PrintList(LinkList *list)
{
Node *p=list->head;
if( p->next==NULL ) cout<<"This is a empty list!"<<endl;
else{
for(int counter=0;counter!=list->length;++counter)//p!=NULL 即counter<list->length
{
p = p->next;
cout<<p->data<<" ";
}
cout<<endl;
}
}

int main()
{
LinkList *list;
int location_find = 1;
int location_insert = 3;
int location_delet = 8;
list=IniList();//生成包含表头节点的空链表
cout<<"The created list is: ";
for(int x=0;x!=10;++x)
CreatList(list,x);
PrintList(list);//输出9 8 7 6 5 4 3 2 1 0

Node *found = GetNode(list, location_find);
cout<<"The 1th number in list is: "<< found->data<<endl;

cout<<"The list after insert at location 3: ";
InsertNode(list,location_insert,18);
PrintList(list);

cout<<"The list after delet at location 8:";
DeletNode(list, location_delet);
PrintList(list);

ClearList(list);
PrintList(list);
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐