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

数据结构实战——线性表的单链表实现

2014-10-31 23:11 393 查看
转载请声明出处:/article/7920491.html

相关概念

前面我们说了线性结构的顺序存储表示(数组表示法),接下来看一下线性结构的链式存储表示(指针表示法)。我们可以发现,顺序表的插入、删除操作平均都要移动约一半的元素。当顺序表的长度非常大时,这个时间的消耗还是不容忽视的,这也是顺序存储的最大缺陷。因此顺序表常常用于那些无需频繁插入和删除元素的应用。而链式存储不再依靠内存的连续性来反映元素之间的逻辑关系,而是通过指针链接体现元素之间的逻辑关系。链表没有了顺序表所要求的存储必须连续的弱点,但也失去了顺序表随机存取任一个元素的优点。

链表的每个存储单元除了要存储数据元素本身之外,还需要存储一个指示直接后继元素的信息--指针。这两部分信息构成一个结点。结点中表示数据元素的域称为数据域,指向直接后继存储位置的域称为指针域。链表的这种存储结构使得链表无需像顺序表那样预先分配好所有的存储空间,而是在插入一个新元素时临时为新的元素分配结点。本篇讲述只包含一个指针域的单链表,后一篇博客讲述双链表。

下图给出了一个节点的示意图:



注意头结点和首元结点的区别,存储的元素,从首元结点开始存储,头结点只为了方便插入/删除操作。



单链表的定义
每个结点由一个结构体表示,其中有数据域 data 和指针域 next。
/* 线性表的单链表实现 -- 定义 */

#ifndef LNODEOPERATION_H_INCLUDED
#define LNODEOPERATION_H_INCLUDED

typedef struct LNODE{
int data;  //数据域,表示当前结点存储的数据元素
LNODE *next;   //指针域,存储直接后继元素的地址
}LNODE;

bool InitList_L(LNODE *&pHead);
void DestroyList_L(LNODE *&pHead);
void ClearList_L(LNODE *pHead);
bool ListEmpty_L(LNODE *pHead);
int ListLength_L(LNODE *pHead);
int GetElem_L(LNODE *pHead, int i, int &e);
int LocateElem_L(LNODE *pHead, int e);
bool PriorElem_L(LNODE *pHead, int cur_e, int &pre_e);
bool NextElem_L(LNODE *pHead, int cur_e, int &next_e);
bool ListInsertFront_L(LNODE *pHead, int i, int x);//前插
bool ListInsertBack_L(LNODE *pHead, int i, int x);//后插
bool ListDelete_L(LNODE *pHead, int i, int &x);
void ListTravers(LNODE *pHead);

#endif // LNODEOPERATION_H_INCLUDED

单链表的12种基本操作
其中需要注意插入新结点时,有前插和后插两种方式,这里我都实现了。代码如下:

/* 线性表的单链表实现 -- 12种基本操作 */

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include "LnodeOperation.h"

bool InitList_L(LNODE *&pHead){ //pHead指向头结点,Head头结点的next指针初始置为NULL
pHead = (LNODE *)malloc(sizeof(LNODE)*1);
if (pHead == NULL)
return false;
pHead->next = NULL;
return true;
}

void DestroyList_L(LNODE *&pHead){ //删除单链表所有结点(包括头结点Head),pHead置为空
ClearList_L(pHead);
free(pHead);
pHead = NULL;
}

void ClearList_L(LNODE *pHead){ //Head头结点的next指针置为空
LNODE *pa,*pb;
pa = pHead->next; //pa指向Head头结点
while (pa != NULL){
pb = pa->next;
free(pa);
pa = pb;
}
pHead->next = NULL;
}

bool ListEmpty_L(LNODE *pHead){//判断链表是否为空
if (pHead->next == NULL)
return true;
return false;
}

int ListLength_L(LNODE *pHead){//返回链表长度
int count = 0;
LNODE *pa = pHead->next;
while(pa != NULL){
count++;
pa = pa->next;
}
return count;
}

int GetElem_L(LNODE *pHead, int i, int &e){//返回链表位序为i的元素
//合法性校验
if (i < 1) return -1;
if (i > ListLength_L(pHead)) return -2;
int count = 0;
LNODE *pa = pHead->next;
while(pa != NULL){
count++;
if (count == i){
e = pa->data;
return 0;
}
pa = pa->next;
}
return -3;
}

int LocateElem_L(LNODE *pHead, int e){//返回单链表中首个e元素的位序
LNODE *pa;
pa = pHead->next;
int count = 0;
while(pa != NULL){
count++;
if (pa->data == e)
return count;
pa = pa->next;
}
return 0;
}

bool PriorElem_L(LNODE *pHead, int cur_e, int &pre_e){//返回cur_e直接前驱
LNODE *pa;
pa = pHead->next;
if (pa == NULL)
return false;
else if (pa->data == cur_e)
return false;
else{
while(pa->next != NULL){
if (pa->next->data == cur_e){
pre_e = pa->data;
return true;
}
pa = pa->next;
}
return false;
}
}

bool NextElem_L(LNODE *pHead, int cur_e, int &next_e){//返回cur_e直接后继
LNODE *pa;
int lastElem;
pa = pHead->next;
if (pa == NULL)
return false;
while(pa->next != NULL){
if (pa->data == cur_e){
next_e = pa->next->data;
return true;
}
pa = pa->next;
}
return false;
}

bool ListInsertFront_L(LNODE *pHead, int i, int x){//在链表第i个结点前面插入x
LNODE *pn, *pa, *pb;
int count = 0;
pn = (LNODE*)malloc(sizeof(LNODE)*1);
if (pn == NULL)
return false;
pa = pHead->next;//pa指向当前第i个结点
pb = pHead;//pb指向当前结点的前驱结点,即第i-1个结点
if (pa == NULL){//如果原链表为空的
pn->data = x;
pn->next = pa;
pb->next = pn;
return true;
}
while(pa != NULL){//否则
count++;
if (count == i)
break;
pb = pa;
pa = pa->next;
}
if (count != i)
return false;//说明需求位序i不合法
pn->data = x;
pn->next = pa;//pn的后继置为pa
pb->next = pn;//pa的原前驱的后继置为pn
return true;
}

bool ListInsertBack_L(LNODE *pHead, int i, int x){//在链表第i个结点后面插入x
LNODE *pn, *pa;
int count = 0;
pn = (LNODE*)malloc(sizeof(LNODE)*1);
if (pn == NULL)
return false;
pa = pHead->next;//pa指向当前第i个结点
if (pa == NULL){//如果链表为空
pn->data = x;
pn->next = pa;
pHead->next = pn;
return true;
}
while(pa != NULL){//否则
count++;
if (count == i)
break;
pa = pa->next;
}
if (count != i)
return false;
pn->data = x;
pn->next = pa->next;//pn的后继置为pa的原后继
pa->next = pn;//pa的后继置为pn
return true;
}

bool ListDelete_L(LNODE *pHead, int i, int &x){//删除链表第i个结点
LNODE *pa, *pb;
int count = 0;
pa = pHead->next;
pb = pHead;
while(pa != NULL){
count++;
if (count == i)
break;
pb = pa;
pa = pa->next;
}
if (count != i)
return false;//位序i不合法
x = pa->data;//保存被删除的结点数据
pb->next = pa->next;//pa的原前驱直接指向pa的后继
free(pa);//释放被删除位置的结点空间
return true;
}

void ListTravers(LNODE *pHead){
LNODE *pa = pHead->next;
while(pa != NULL){
printf("%d ",pa->data);
pa = pa->next;
}
printf("\n");
}


单链表的主程序实现
基于上述实现的单链表的12种功能,如下进行调用调试。
/**
@author:lichunchun
@date:2015/3/25
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "LnodeOperation.h"

int main(){
LNODE *pHead;
char str[30],*th;
int temp,select,item,length=0;
printf("(1)初始化单链表h\n");
InitList_L(pHead);
printf("(2)请给出将要插入链表中的元素:");
gets(str);
printf("(3)依次用后插法插入上述元素\n");
th = strtok(str," ");
while(th != NULL){
temp = atoi(th);
if (!ListInsertBack_L(pHead,length++,temp))
return 0;
th = strtok(NULL," ");
}
printf("(4)输出单链表h:");
ListTravers(pHead);
printf("(5)单链表h长度=%d\n",ListLength_L(pHead));
printf("(6)单链表h为%s\n",(ListEmpty_L(pHead))?"空":"非空");
printf("(7)您想获取这个链表的第几个元素?");
scanf("%d",&select);
if (!GetElem_L(pHead,select,item))
printf("(8)单链表h的第%d个元素=%d\n",select,item);
else
printf("(8)非法位序!\n");
printf("(9)元素2的位序=%d\n",LocateElem_L(pHead,2));
printf("(10)在第3个元素后面插入元素10\n");
ListInsertBack_L(pHead,3,10);
printf("(11)在第1个元素前面插入元素11\n");
ListInsertFront_L(pHead,1,11);
printf("(12)输出单链表h:");
ListTravers(pHead);
printf("(13)删除单链表h的第2个元素\n");
ListDelete_L(pHead,2,item);
printf("(14)输出单链表h:");
ListTravers(pHead);
printf("(15)释放单链表h\n");
DestroyList_L(pHead);
return 0;
}

程序运行结果,如下图所示:



是不是很简单啊?后一篇博客我们在来玩一玩双向链表~


转载请声明出处:/article/7920491.html

源码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: