LinkedList.h
/* 线性表的链式存储结构 */
typedef int ElemType ; //ElemType类型根据实际情况而定,这里假设为int
typedef struct Node
{
ElemType data;
struct Node * next;
/* 线性表的链式存储结构 */
/* 线性表的链式存储结构 */
/* 线性表的链式存储结构 */
/* 线性表的链式存储结构 */
/* 线性表的链式存储结构 */
typedef int ElemType ; //ElemType类型根据实际情况而定,这里假设为int
typedef struct Node
{
ElemType data;
struct Node * next;
}Node,*LinkedList;
#define OK 1
#define ERROR 0
typedef int Status;
/* 线性表通用操作 */
Status InitList(LinkedList *L); //建立一个空的线性表L
Status ListEmpty(LinkedList L); //若线性表为空返回true,否则返回false
Status ClearList(LinkedList *L); //将线性表清空
Status GetElem(LinkedList L,int i,ElemType *e); //将线性表L中第i个位置元素值返回给e
int LocateElem(LinkedList L,ElemType e); //在线性表L中查找与给定值e相等的元素,如果查找>成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
Status ListInsert(LinkedList *L,int i,ElemType e); //在线性表L中的第i个位置插入新元素e
Status ListDelete(LinkedList *L,int i,ElemType *e); //删除线性表L中第i个位置元素,并用e返回值值
int ListLength(LinkedList L); //返回线性表L的元素个数
LinkedList.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "LinkedList.h"
int main(void)
{
LinkedList L;
InitList(&L);
if(ListEmpty(L))
{
printf("是空的\n");
ListInsert(&L,0,3);
ListInsert(&L,1,13);
ListInsert(&L,2,23);
ListInsert(&L,2,33);
ListInsert(&L,4,43);
ListInsert(&L,5,53);
ListInsert(&L,6,63);
ListInsert(&L,7,73);
ListInsert(&L,8,83);
ListInsert(&L,9,93);
ListInsert(&L,10,103);
ListInsert(&L,11,113);
ListInsert(&L,12,123);
}
printf("遍历所有元素:\n");
int len = ListLength(L);
LinkedList h;int j=0;
h = L;
while(h->next)
{
h = h->next;
printf("[%d]%d,",j,h->data);
j++;
}
printf("\n长度为%d\n",len);
ElemType e;
GetElem(L,2,&e);
printf("索引2位置的元素是:%d\n",e);
printf("查找53的索引位置是:%d\n",LocateElem(L,53));
printf("删除第6位后\n");
ElemType ee;
ListDelete(&L,6,&ee);
len = ListLength(L);
j = 0;
h = L;
while(h->next)
{
h = h->next;
printf("[%d]%d,",j,h->data);
j++;
}
printf("\n");
return 0;
}
//建立一个空的线性表L
Status InitList(LinkedList *L)
{
*L = (LinkedList)malloc(sizeof(LinkedList));
(*L)->next = NULL;
(*L)->data = 0;
return OK;
}
//若线性表为空返回true,否则返回false
Status ListEmpty(LinkedList L)
{
return L->next == NULL;
}
//将线性表清空
Status ClearList(LinkedList *L)
{
LinkedList p = (*L)->next,q;
while(p)
{
q = p->next;
free(p);
p = q;
}
(*L)->next = NULL; //头结点的指针域置空
(*L)->data = 0; //头结点的数据域表示长度变为0
return OK;
}
//将线性表L中第i个位置元素值返回给e
Status GetElem(LinkedList L,int i,ElemType *e)
{
int j=0;
LinkedList p = L->next;//第一个结点
while(p && j<i)
{
p = p->next;
j++;
}
if(!p || j>i)
{
printf("第%d个结点不存在\n",i);
return ERROR;
}
*e = p->data;
return OK;
}
//在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
int LocateElem(LinkedList L,ElemType e)
{
int j =0;
LinkedList p = L->next;
while(p)
{
if(p->data == e)
{
return j;
}
p = p->next;
j++;
}
return ERROR;
}
//在线性表L中的第i个位置插入新元素e
Status ListInsert(LinkedList *L,int i,ElemType e)
{
int j=0;
LinkedList p,s; //s为新建的结点的指针
p = *L; //p指向表头结点
while(p && j<i)
{
p = p->next; //往后找
j++;
}
if(!p || j>i)
{
printf("第%d个结点不存在!\n",i);
return ERROR;
}
s = (LinkedList )malloc(sizeof(Node));
s->data = e;
s->next = p->next;
p->next = s;
(*L)->data ++;
return OK;
}
//删除线性表L中第i个位置元素,并用e返回值值
Status ListDelete(LinkedList *L,int i,ElemType *e)
{
LinkedList p = (*L)->next,q;
int j = 0;
while(p && j<i-1)
{
p = p->next;
j++;
}
if(!(p->next) || j>i)
{
printf("第%d个结点不存在\n",i);
return ERROR;
}
q = p->next;
p->next = q->next;
*e = q->data;
free(q);
return OK;
}
//返回线性表L的元素个数
int ListLength(LinkedList L)
{
return L->data;
}
总结:
线性表的查找、插入与删除时间复杂度都为O(n)
如果在不知道第i个元素的指针位置,单链表数据结构在插入和删除操作上,与线性表的顺序存储结构没有太大优势。但如果希望从第i个位置,插入10个元素,对于顺序存储结构意味着,每一次插入都需要移动n-1个元素,每次都是O(n)。则单链表只需在第一次时,找个第i个指针,此时为O(n),接下来只是简单地通过赋值移动指针而已,时间复杂度都是O(1)。显然对于插入或删除数据越频繁的操作,单链表的效率优势就越明显。
单链表的整表创建与删除
LinkedList2.c
/* 单链表的整表创建与删除 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "LinkedList.h"
/* 随机产生n个元素的值,建立带头结点的单链表L(头插法) */
Status CreateListHead(LinkedList *L,int n);
/* 随机产生n个元素的值,建立带头结点的单链表L(尾插法) */
Status CreateListTail(LinkedList *L,int n);
void printList(LinkedList L);
/* 初始条件:链表已存在,操作结果:将L重置为空表 */
Status ClearList(LinkedList *L);
int main(void)
{
LinkedList L;
printf("头插法创建单链表:\n");
CreateListHead(&L,10);
printList(L);
printf("清空后:\n");
ClearList(&L);
printList(L);
printf("尾插法创建单链表:\n");
CreateListTail(&L,10);
printList(L);
printf("清空后:\n");
ClearList(&L);
printList(L);
return 0;
}
/* 随机产生n个元素的值,建立带头结点的单链表L(头插法) */
Status CreateListHead(LinkedList *L,int n)
{
LinkedList p;
int i;
srand(time(0)); //初始化随机种子数
*L = (LinkedList)malloc(sizeof(Node));
(*L)->next = NULL; //建立一个带头结点的单链表
for(i=0;i<n;i++)
{
p = (LinkedList)malloc(sizeof(Node)); //生成新结点
p->data = rand()%100+1;
p->next = (*L)->next;
(*L)->next = p; //插入到表头
}
return OK;
}
/* 随机产生n个元素的值,建立带头结点的单链表L(尾插法) */
Status CreateListTail(LinkedList *L,int n)
{
LinkedList p,r;
int i;
srand(time(0)); //初始化随机种子数
*L = (LinkedList)malloc(sizeof(Node));
(*L)->next = NULL; //建立一个带头结点的单链表
r = (*L); //r指向尾部的结点
for(i=0;i<n;i++)
{
p = (LinkedList)malloc(sizeof(Node)); //生成新结点
p->data = rand()%100+1;
r->next = p; //插入到尾部
r = p; //重新指向尾部
}
r->next = NULL; //表示当前链表结束
return OK;
}
void printList(LinkedList L)
{
LinkedList p = L->next;
while(p)
{
printf("%d,",p->data);
p = p->next;
}
printf("\n");
}
/* 初始条件:链表已存在,操作结果:将L重置为空表 */
Status ClearList(LinkedList *L)
{
LinkedList p = (*L)->next,q; //定义一个指向头结点的指针
while(p)
{
q = p->next; //找到下一个结点
free(q); //释放该结点
p = q; //指向下一下结点
}
(*L)->next = NULL; //头结点指向为空
return OK;
}
链式存储结构与顺序存储结构的优缺点
存储分配方式 | 时间性能 | 空间性能 |
·顺序存储结构用一段连续的存储单元依次存储线性表的数据元素 ·单链表采用链式存储结构,用一组任意的存储单元存放线性表的无数 | ·查找 ·顺序存储结构O(1) ·单链表O(n) ·插入和删除 ·顺序存储结构需要平均移动表长一半的元素时间为O(n) ·单链表在找到某位置的指针后,插入和删除时仅为O(1) | ·顺序存储结构需要分配存储空间。分大了浪费,分小了易发生上溢 ·单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制 |
通过上面的对比,得出一些结论
·若线性表需要频繁查找,很少进行插入和删除时,宜采用顺序存储结构。若需要频繁插入和删除时,宜采用单链表结构。
·当线性表中的元素个数变化较大或根本不知道有多大时,最好采用单链表结构,这样可以不需要考虑存储空间的大小问题。而如果事先知道线性表的大致长度,这种用顺序存储结构效率会高很多。