概述
数据结构的学习当然要从线性表学起,而线性表里首先需要学习单链表,这里从单链表最简单的顺序存储结构(本质就是可变数组存储)开始。
解析
单链表强调元素在逻辑上紧密相邻,所以首先想到用数组存储。但是普通数组有着无法克服的容量限制,在不知道输入有多少的情况下,很难确定出一个合适的容量。对此,一个较好的解决方案就是使用动态数组。首先用malloc申请一块拥有指定初始容量的内存,这块内存用作存储单链表元素,当录入的内容不断增加,以至于超出了初始容量时,就用calloc扩展内存容量,这样就做到了既不浪费内存,又可以让单链表容量随输入的增加而自适应大小。
单链表顺序存储结构如下图:
可能涉及到的语法难点
刚接触数据结构的同学,单链表顺序存储结构可能会是其面对的第一个坎。这里涉及到了结构体、动态数组、结构指针,甚至还有函数变量(函数做参数,本质是函数指针),所以需要有相对扎实的语言语法基础。当然,这也并不是说一定得掌握了高级语法才能开始学习数据结构,可以先将语言学到入门(入门意味着学会了提问),再边学数据结构边巩固语法。一定要亲自动手写一写,否则,肯定学不好。
源码 SequenceList-mian.c
/***************************************
* *
* 文件夹: ▲02 线性表\01 SequenceList *
* *
* 内 容: 单链表顺序结构相关函数测试 *
* *
***************************************/
#include <stdio.h>
#include "SequenceList.c" //**▲02 线性表**//
/* 测试调用的函数原型 */
Status CmpGreater(LElemType_Sq e, LElemType_Sq data);
//若data>e,返回TRUE,否则返回FALSE
void PrintElem(LElemType_Sq e);
//测试函数,打印整型
int main(int argc, char **argv)
{
SqList L;
int i;
LElemType_Sq e;
printf("▼1\n▲函数 InitList_Sq 测试...\n"); //1.函数InitList_Sq测试
{
printf("初始化顺序表 L ...\n");
InitList_Sq(&L);
printf("\n");
}
PressEnter;
printf("▼4\n▲函数 ListEmpty_Sq 测试...\n"); //4.函数ListEmpty_Sq测试
{
ListEmpty_Sq(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
printf("\n");
}
PressEnter;
printf("▼10\n▲函数 ListInsert_Sq 测试...\n"); //10.函数ListInsert_Sq测试
{
for(i=1; i<=6; i++)
{
printf("作为示范,在 L 第 %d 个位置插入 \"%d\"...\n", i, 2*i);
ListInsert_Sq(&L, i, 2*i);
}
printf("\n");
}
PressEnter;
printf("▼12\n▲函数 ListTraverse_Sq 测试...\n"); //12.函数ListTraverse_Sq测试
{
printf(" L 中的元素为:L = ");
ListTraverse_Sq(L, PrintElem);
printf("\n\n");
}
PressEnter;
printf("▼5\n▲函数 ListLength_Sq 测试...\n"); //5.函数ListLength_Sq测试
{
i = ListLength_Sq(L);
printf(" L 的长度为 %d \n", i);
printf("\n");
}
PressEnter;
printf("▼11\n▲函数 ListDelete_Sq 测试...\n"); //11.函数ListDelete_Sq测试
{
ListDelete_Sq(&L, 6, &e);
printf("删除 L 中第 6 个元素 \"%d\" ...\n", e);
printf(" L 中的元素为:L = ");
ListTraverse_Sq(L, PrintElem);
printf("\n\n");
}
PressEnter;
printf("▼6\n▲函数 GetElem_Sq 测试...\n"); //6.函数GetElem_Sq测试
{
GetElem_Sq(L, 4, &e);
printf(" L 中第 4 个位置的元素为 \"%d\" \n", e);
printf("\n");
}
PressEnter;
printf("▼7\n▲函数 LocateElem_Sq 测试...\n"); //7.函数LocateElem_Sq测试
{
i = LocateElem_Sq(L, 7, CmpGreater);
printf(" L 中第一个元素值大于 \"7\" 的元素的位置为 %d \n", i);
printf("\n");
}
PressEnter;
printf("▼8\n▲函数 PriorElem_Sq 测试...\n"); //8.函数PriorElem_Sq测试
{
PriorElem_Sq(L, 6, &e);
printf("元素 \"6\" 的前驱为 \"%d\" \n", e);
printf("\n");
}
PressEnter;
printf("▼9\n▲函数 NextElem_Sq 测试...\n"); //9.函数NextElem_Sq测试
{
NextElem_Sq(L, 6, &e);
printf("元素 \"6\" 的后继为 \"%d\" \n", e);
printf("\n");
}
PressEnter;
printf("▼2\n▲函数 ClearList_Sq 测试...\n"); //2.函数ClearList_Sq测试
{
printf("清空 L 前:");
ListEmpty_Sq(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
ClearList_Sq(&L);
printf("清空 L 后:");
ListEmpty_Sq(L) ? printf(" L 为空!!\n") : printf(" L 不为空!\n");
printf("\n");
}
PressEnter;
printf("▼3\n▲函数 DestroyList_Sq 测试...\n"); //3.函数DestroyList_Sq测试
{
printf("销毁 L 前:");
L.elem ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
DestroyList_Sq(&L);
printf("销毁 L 后:");
L.elem ? printf(" L 存在!\n") : printf(" L 不存在!!\n");
printf("\n");
}
PressEnter;
return 0;
}
Status CmpGreater(LElemType_Sq e, LElemType_Sq data)
{
return data>e ? TRUE : FALSE;
}
void PrintElem(LElemType_Sq e)
{
printf("%d ", e);
}
源码 SequenceList.c
/***************************************
* *
* 文件夹: ▲02 线性表\01 SequenceList *
* *
* 文件名: SequenceList.c *
* *
* 算 法: 2.3、2.4、2.5、2.6 *
* *
***************************************/
#ifndef SEQUENCELIST_C
#define SEQUENCELIST_C
#include "SequenceList.h" //**▲02 线性表**//
/*════╗
║算法2.3 ║
╚════*/
Status InitList_Sq(SqList *L)
{
(*L).elem = (LElemType_Sq*)malloc(LIST_INIT_SIZE*sizeof(LElemType_Sq));
if(!(*L).elem)
exit(OVERFLOW); //分配内存失败
(*L).length = 0; //初始化顺序表长度为0
(*L).listsize = LIST_INIT_SIZE; //顺序表初始内存分配量
return OK; //初始化成功
}
void ClearList_Sq(SqList *L)
{
(*L).length = 0;
}
void DestroyList_Sq(SqList *L)
{
free((*L).elem);
(*L).elem = NULL; //释放内存后置空指针
(*L).length = 0;
(*L).listsize = 0;
}
Status ListEmpty_Sq(SqList L)
{
return L.length==0 ? TRUE : FALSE;
}
int ListLength_Sq(SqList L)
{
return L.length;
}
Status GetElem_Sq(SqList L, int i, LElemType_Sq *e)
{
if(i<1 || i>L.length)
return ERROR; //i值不合法
else
*e = L.elem[i-1];
return OK;
}
/*════╗
║算法2.6 ║
╚════*/
int LocateElem_Sq(SqList L, LElemType_Sq e, Status(Compare)(LElemType_Sq, LElemType_Sq))
{
int i = 1; //i的初值为第一个元素的位序
while(i<=L.length && !Compare(e, L.elem[i-1]))
++i;
if(i<=L.length)
return i;
else
return 0;
}
Status PriorElem_Sq(SqList L, LElemType_Sq cur_e, LElemType_Sq *pre_e)
{
int i = 1;
if(L.elem[0]!=cur_e) //第一个结点无前驱
{
while(i<L.length && L.elem[i]!=cur_e)
++i;
if(i<L.length)
{
*pre_e = L.elem[i-1];
return OK;
}
}
return ERROR;
}
Status NextElem_Sq(SqList L, LElemType_Sq cur_e, LElemType_Sq *next_e)
{
int i = 0;
while(i<L.length && L.elem[i]!=cur_e)
++i;
if(i<L.length-1) //最后一个结点无后继
{
*next_e = L.elem[i+1];
return OK;
}
return ERROR;
}
/*════╗
║算法2.4 ║
╚════*/
Status ListInsert_Sq(SqList *L, int i, LElemType_Sq e)
{
LElemType_Sq *newbase;
LElemType_Sq *p, *q;
if(i<1 || i>(*L).length+1)
return ERROR; //i值不合法
if((*L).length >= (*L).listsize) //若存储空间已满,需开辟新空间
{
newbase = (LElemType_Sq*)realloc((*L).elem, ((*L).listsize+LISTINCREMENT)*sizeof(LElemType_Sq));
if(!newbase)
exit(OVERFLOW);
(*L).elem = newbase;
(*L).listsize += LISTINCREMENT;
}
q = &(*L).elem[i-1]; //q为插入位置
for(p=&(*L).elem[(*L).length-1]; p>=q; --p)
*(p+1) = *p; //插入位置及之后的元素右移
*q = e; //插入e
(*L).length++; //表长增1
return OK;
}
/*════╗
║算法2.5 ║
╚════*/
Status ListDelete_Sq(SqList *L, int i, LElemType_Sq *e)
{
LElemType_Sq *p, *q;
if(i<1 || i>(*L).length)
return ERROR; //i值不合法
p = &(*L).elem[i-1]; //p为被删除元素的位置
*e = *p;
q = (*L).elem+(*L).length-1; //表尾元素位置
for(++p; p<=q; ++p)
*(p-1) = *p; //被删元素之后的元素左移
(*L).length--; //表长减1
return OK;
}
Status ListTraverse_Sq(SqList L, void(Visit)(LElemType_Sq))
{
int i;
for(i=0; i<L.length; i++)
Visit(L.elem[i]);
return OK;
}
#endif
源码SequenceList.h
/***************************************
* *
* 文件夹: ▲02 线性表\01 SequenceList *
* *
* 文件名: SequenceList.h *
* *
* 内 容: 顺序表相关操作列表 *
* *
***************************************/
#ifndef SEQUENCELIST_H
#define SEQUENCELIST_H
#include <stdio.h>
#include <stdlib.h> //提供malloc、realloc、free、exit原型
#include "../../▲01 绪论/Status.h" //**▲01 绪论**//
/* 宏定义 */
#define LIST_INIT_SIZE 100 //顺序表存储空间的初始分配量
#define LISTINCREMENT 10 //顺序表存储空间的分配增量
/* 顺序表类型定义 */
#ifndef LELEMTYPE_SQ
#define LELEMTYPE_SQ
typedef int LElemType_Sq;
#endif
typedef struct
{
LElemType_Sq *elem; //存储空间基址(指向第一个结点的指针)
int length; //当前顺序表长度
int listsize; //当前分配的存储容量
}SqList; //顺序表0号单元正常使用
/* 顺序表函数列表 */
Status InitList_Sq(SqList *L);
/*━━━━━━━━━━━━━━━┓
┃(01)算法2.3:初始化空顺序表L。┃
┗━━━━━━━━━━━━━━━*/
void ClearList_Sq(SqList *L);
/*━━━━━━━━━┓
┃(02)清空顺序表L。 ┃
┗━━━━━━━━━*/
void DestroyList_Sq(SqList *L);
/*━━━━━━━━━┓
┃(03)销毁顺序表L。 ┃
┗━━━━━━━━━*/
Status ListEmpty_Sq(SqList L);
/*━━━━━━━━━━━━━┓
┃(04)判断顺序表L是否为空。 ┃
┗━━━━━━━━━━━━━*/
int ListLength_Sq(SqList L);
/*━━━━━━━━━━━━━━┓
┃(05)返回顺序表L中元素个数。 ┃
┗━━━━━━━━━━━━━━*/
Status GetElem_Sq(SqList L, int i, LElemType_Sq *e);
/*━━━━━━━━━━━━━━━━┓
┃(06)用e接收顺序表L中第i个元素。 ┃
┗━━━━━━━━━━━━━━━━*/
int LocateElem_Sq(SqList L, LElemType_Sq e, Status(Compare)(LElemType_Sq, LElemType_Sq));
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃(07)算法2.6:返回顺序表L中首个与e满足Compare关系的元素位序。┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
Status PriorElem_Sq(SqList L, LElemType_Sq cur_e, LElemType_Sq *pre_e);
/*━━━━━━━━━━━━━━━━━┓
┃(08)用pre_e接收cur_e的前驱。 ┃
┗━━━━━━━━━━━━━━━━━*/
Status NextElem_Sq(SqList L, LElemType_Sq cur_e, LElemType_Sq *next_e);
/*━━━━━━━━━━━━━━━━━┓
┃(09)用next_e接收cur_e的后继。 ┃
┗━━━━━━━━━━━━━━━━━*/
Status ListInsert_Sq(SqList *L, int i, LElemType_Sq e);
/*━━━━━━━━━━━━━━━━━━━━━┓
┃(10)算法2.4:在顺序表L的第i个位置上插入e。┃
┗━━━━━━━━━━━━━━━━━━━━━*/
Status ListDelete_Sq(SqList *L, int i, LElemType_Sq *e);
/*━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃(11)算法2.5:删除顺序表L上第i个位置的元素,并用e返回。┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━*/
Status ListTraverse_Sq(SqList L, void (Visit)(LElemType_Sq));
/*━━━━━━━━━━━━━━┓
┃(12)用visit函数访问顺序表L。┃
┗━━━━━━━━━━━━━━*/
#endif
本文章转载于博客园- 康建伟。如果侵犯你的权益请联系我下架