数据结构-单链表

文章目录

  • 链表概念
  • 单链表声明 LinkList.h
  • 单链表定义
    • 初始化单链表的头结点 InitList_Link
    • 判断一个链表是否是空表ListEmpty_Link
    • 求链表长度:ListLength_Link
    • 取第i个元素的值:GetElem_List
    • 向第i个位置上插入结点,值为e
    • 删除第i个结点
    • 构造链表
    • 遍历链表
    • 查找
    • 清空:将链表L清空,就是将L变成一个空表
    • 销毁:销毁链表L
    • 逆置单链表L
  • 声明汇总 LinkList.c


链表概念

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。


单链表声明 LinkList.h

#include 
#include 

#ifndef _LINKLIST_H
#define _LINKLIST_H
typedef int Status;  //Status类型就是int型
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1

typedef int ElemType;   //结点的元素类型为int型
typedef struct LNode{   //单链表的结构体
 	ElemType   data;  //数据域,存储本结点的信息(数据)
 	struct LNode  *next;  //指针域,指向下一个结点的地址
}LNode, *LinkList;
//1、初始化单链表,就是创建出头结点
Status InitList_Link(LinkList &L);
//(2)判断一个链表是否是空表ListEmpty_Link
Status ListEmpty_Link(LinkList L);
//(3)求长度:ListLength_Link
int ListLength_Link_1(LinkList L); //方法一:p从L开始,即头结点
int ListLength_Link_2(LinkList L); //方法二:p从L->next开始,即第一个真正的元素结点
//(4)取第i个元素的值:GetElem_List
Status GetElem_List(LinkList L,int i,ElemType &e);
(5)向第i个位置上插入结点,值为e
Status ListInsert_Link(LinkList &L,int i,ElemType e);
//(6)删除第i个结点,注意如果i值太大或太小,删除失败,返回ERROR
Status ListDelete_Link(LinkList &L,int i,ElemType &e);
//(7)构造链表
 //方法一:每次插入到最末尾,向尾生长,所得到的顺序与插入时的元素顺序一致。
Status CreateList_Link_Rear(LinkList &L,ElemType a[],int n);
 //方法二:每次插入到第1个结点,就是直接插入到头结点之后,向头生长,所得到的顺序与插入时的元素顺序相反
Status CreateList_Link_Head(LinkList &L,ElemType a[],int n);
//(8)遍历链表:从表头到表尾,一个一个结点进行访问
Status TraverseList_Link(LinkList L);
//(9)查找:从链表L中查找与指定元素x相同的结点,如果存在,返回该结点指针,如果不存在,返回NULL
LinkList SearchList_Link(LinkList L,ElemType x);
//(10)清空:将链表L清空,就是将L变成一个空表
//方法一:调用前面的Delete函数,每次删除表头结点(就是删除第一个结点),这样,删除了第一个结点后,原来的第二个结点就变成了新的第一个结点,以此类推,一直删除下去,直到链表为空:
Status ClearList_Link_1(LinkList &L);
//方法二:每次都删除第1个结点,直到链表为空
Status ClearList_Link_2(LinkList &L);
//(11)销毁:销毁链表L
Status DestroyList_Link(LinkList &L);
//逆置单链表L
Status InverseList_Link(LinkList &L);

#endif

单链表定义

初始化单链表的头结点 InitList_Link

//1、初始化单链表的头结点
Status InitList_Link(LinkList &L)
{
	L=(LinkList)malloc(sizeof(LNode));	
	L->next=NULL;  //因为L是指针,所以用->符号,也可以用:
 	//(*L).next=NULL;
 	return OK;
}

判断一个链表是否是空表ListEmpty_Link

//(2)判断一个链表是否是空表ListEmpty_Link
Status ListEmpty_Link(LinkList L)
{
 return L->next==NULL ? TRUE : FALSE;
}

求链表长度:ListLength_Link

代码如下(示例):

//(3)求长度:ListLength_Link
Status ListLength_Link_1(LinkList L) //方法一:p从L开始,即头结点
{
 	int n=0;
 	LinkList p=L;
 	while(p->next!=NULL)
 	{
 		n++;
  		p=p->next;
 	}
 	return n;
}
Status ListLength_Link_2(LinkList L) //方法二:p从L->next开始,即第一个真正的元素结点
{
 	int n=0;
 	LinkList p=L->next;
 	while(p!=NULL)
 	{
	  	n++;
	  	p=p->next;
 	}
 	return n;
}

取第i个元素的值:GetElem_List

Status GetElem_List(LinkList L,int i,ElemType &e)
{
	LinkList p;
 	int n;
 	p=L;
 	n=0;
 	while(p->next!=NULL && n<i)
 	{
	  	p=p->next;
	  	n++;
	 }
	 if(n<i)
	  	return  ERROR;
	 e=p->data;	//获取的值
	 return  OK;
}

向第i个位置上插入结点,值为e

Status ListInsert_Link(LinkList &L,int i,ElemType e)
{ //向链表L的第i个位置插入元素e
 	 LinkList p;
	 int n;
	 p=L;
	 n=0;
	 while(p!=NULL && n<i-1) //寻找p,让p指向第i-1个元素
	 {
		  p=p->next;
		  n++;
	 }
	 if(p==NULL || n>=i)  //i的值太大或太小,插入位置非法
	 	 return ERROR;
	 LinkList s;
	 s=(LinkList)malloc(sizeof(LNode));
	 s->data=e;
	 s->next=p->next;
	 p->next=s;
	 return OK;
}

删除第i个结点

//(6)删除第i个结点,注意如果i值太大或太小,删除失败,返回ERROR
Status ListDelete_Link(LinkList &L,int i,ElemType &e)
{
	 //先找到第i-1个结点,由p指向它
	 LinkList p,s;
	 int n;
	 p=L;
	 n=0;
	 while(p!=NULL && n<i-1) //寻找p,让p指向第i-1个元素
	 {
		  p=p->next;
		  n++;
	 }
	 if(p->next==NULL || n>i-1) //如果i的值太大或太小,无法删除
	  	return ERROR;
	 s=p->next;
	 p->next=s->next;
	 e=s->data;
	 free(s);
	 return OK;
	}

构造链表

//方法一:每次插入到最末尾,向尾生长,所得到的顺序与插入时的元素顺序一致。
Status CreateList_Link_Rear(LinkList &L,ElemType a[],int n)
{ //向尾生长方式,将有n个元素的数组a中的元素生成链表L
	 L=(LinkList)malloc(sizeof(LNode));
	 L->next=NULL;
	 LinkList p=L;  //让p始终指向表尾,方便插入操作
	 LinkList s;
	 int i;
	 for(i=0;i<n;i++)
	 {
		  s=(LinkList)malloc(sizeof(LNode));
		  s->data=a[i];
		  s->next=NULL;
		  p->next=s;
		  p=s;
	 }
	 return OK;
}
 //方法二:每次插入到第1个结点,就是直接插入到头结点之后,向头生长,所得到的顺序与插入时的元素顺序相反
Status CreateList_Link_Head(LinkList &L,ElemType a[],int n)
{ //向头生长方式,将有n个元素的数组a中的元素生成链表L
	 L=(LinkList)malloc(sizeof(LNode));
	 L->next=NULL;
	 LinkList s;
	 int i;
	 for(i=0;i<n;i++)
 	 {
		  s=(LinkList)malloc(sizeof(LNode)); //生成新结点
		  s->data=a[i];
		  s->next=L->next;     //s作为第一个结点,就是直接插入到头结点的后面
		  L->next=s;
	 }
	 return OK;
}

遍历链表

//(8)遍历链表:从表头到表尾,一个一个结点进行访问
Status TraverseList_Link(LinkList L)
{
	 LinkList p=L->next;
	 while(p!=NULL)
	 {
	  printf("%5d",p->data);  //输出p的值
	  p=p->next;
	 }
	 putchar('\n');
 	return OK;
}

查找

//(9)查找:从链表L中查找与指定元素x相同的结点,如果存在,返回该结点指针,如果不存在,返回NULL
LinkList SearchList_Link(LinkList L,ElemType x)
{
 LinkList p=L->next;
 while(p!=NULL)
 {
  if(p->data==x)
   break;
  p=p->next;
 }
 return p;
}

清空:将链表L清空,就是将L变成一个空表

//方法一:调用前面的Delete函数,每次删除表头结点(就是删除第一个结点),这样,删除了第一个结点后,原来的第二个结点就变成了新的第一个结点,以此类推,一直删除下去,直到链表为空:
Status ClearList_Link_1(LinkList &L)
{
 ElemType e;
 while(!ListEmpty_Link(L))  //一直删除,直到链表为空
  ListDelete_Link(L,1,e);  //删除第一个结点
 return OK;
}
//方法二:每次都删除第1个结点,直到链表为空
Status ClearList_Link_2(LinkList &L)
{
 LinkList p=L->next;  //p始终指向第一个结点,就是L后面的
 while(p!=NULL)
 {
  L->next=p->next;  //L->next绕过p,指向p后面的那个结点
  free(p);    //删除p
  p=L->next;    //p始终指向第一个结点,就是L后面的
 }
 return OK;
}

销毁:销毁链表L

Status DestroyList_Link(LinkList &L)
{
 ClearList_Link_1(L);  //先将链表清空
 free(L);     //然后再将头结点释放
 L=NULL;     //释放了的指针,通常应该让它指向NULL
 return OK;
}

逆置单链表L

Status InverseList_Link(LinkList &L)
{
 LinkList M;
 M=(LinkList)malloc(sizeof(LNode)); //先生成一个头结点M
 M->next=NULL;
 LinkList p=L->next;
 while(p!=NULL)
 {
  L->next=p->next; //先让p从L中脱落,就是让L绕过p
  p->next=M->next; //将p结点向头生长插入到M的第一个结点
  M->next=p; 
  p=L->next;   //p又指向L中的第一个结点
 }
 free(L);
 L=M;
 return OK;
}

声明汇总 LinkList.c

#include "LinkList.h"
#include 
#include 

//1、初始化单链表的头结点
Status InitList_Link(LinkList &L)
{
 L=(LinkList)malloc(sizeof(LNode));
 L->next=NULL;  //因为L是指针,所以用->符号,也可以用:
 //(*L).next=NULL;
 return OK;
}
//(2)判断一个链表是否是空表ListEmpty_Link
Status ListEmpty_Link(LinkList L)
{
 return L->next==NULL ? TRUE : FALSE;
 // return !(L->next);
 // if(L->next==NULL)
 //  return TRUE;
 // else
 //  return FALSE;
}
//(3)求长度:ListLength_Link
Status ListLength_Link_1(LinkList L) //方法一:p从L开始,即头结点
{
 int n=0;
 LinkList p=L;
 while(p->next!=NULL)
 {
  n++;
  p=p->next;
 }
 return n;
}
Status ListLength_Link_2(LinkList L) //方法二:p从L->next开始,即第一个真正的元素结点
{
 int n=0;
 LinkList p=L->next;
 while(p!=NULL)
 {
  n++;
  p=p->next;
 }
 return n;
}
//(4)取第i个元素的值:GetElem_List
Status GetElem_List(LinkList L,int i,ElemType &e)
{
 LinkList p;
 int n;
 p=L;
 n=0;
 while(p->next!=NULL && n<i)
 {
  p=p->next;
  n++;
 }
 if(n<i)
  return  ERROR;
 e=p->data;
 return  OK;
}
//(5)向第i个位置上插入结点,值为e
Status ListInsert_Link(LinkList &L,int i,ElemType e)
{ //向链表L的第i个位置插入元素e
 LinkList p;
 int n;
 p=L;
 n=0;
 while(p!=NULL && n<i-1) //寻找p,让p指向第i-1个元素
 {
  p=p->next;
  n++;
 }
 if(p==NULL || n>=i)  //i的值太大或太小,插入位置非法
  return ERROR;
 LinkList s;
 s=(LinkList)malloc(sizeof(LNode));
 s->data=e;
 s->next=p->next;
 p->next=s;
 return OK;
}
//(6)删除第i个结点,注意如果i值太大或太小,删除失败,返回ERROR
Status ListDelete_Link(LinkList &L,int i,ElemType &e)
{
 //先找到第i-1个结点,由p指向它
 LinkList p,s;
 int n;
 p=L;
 n=0;
 while(p!=NULL && n<i-1) //寻找p,让p指向第i-1个元素
 {
  p=p->next;
  n++;
 }
 if(p->next==NULL || n>i-1) //如果i的值太大或太小,无法删除
  return ERROR;
 s=p->next;
 p->next=s->next;
 e=s->data;
 free(s);
 return OK;
}
//(7)构造链表
 //方法一:每次插入到最末尾,向尾生长,所得到的顺序与插入时的元素顺序一致。
Status CreateList_Link_Rear(LinkList &L,ElemType a[],int n)
{ //向尾生长方式,将有n个元素的数组a中的元素生成链表L
 L=(LinkList)malloc(sizeof(LNode));
 L->next=NULL;
 LinkList p=L;  //让p始终指向表尾,方便插入操作
 LinkList s;
 int i;
 for(i=0;i<n;i++)
 {
  s=(LinkList)malloc(sizeof(LNode));
  s->data=a[i];
  s->next=NULL;
  p->next=s;
  p=s;
 }
 return OK;
}
 //方法二:每次插入到第1个结点,就是直接插入到头结点之后,向头生长,所得到的顺序与插入时的元素顺序相反
Status CreateList_Link_Head(LinkList &L,ElemType a[],int n)
{ //向头生长方式,将有n个元素的数组a中的元素生成链表L
 L=(LinkList)malloc(sizeof(LNode));
 L->next=NULL;
 LinkList s;
 int i;
 for(i=0;i<n;i++)
 {
  s=(LinkList)malloc(sizeof(LNode)); //生成新结点
  s->data=a[i];
  s->next=L->next;     //s作为第一个结点,就是直接插入到头结点的后面
  L->next=s;
 }
 return OK;
}
//(8)遍历链表:从表头到表尾,一个一个结点进行访问
Status TraverseList_Link(LinkList L)
{
 LinkList p=L->next;
 while(p!=NULL)
 {
  printf("%5d",p->data);  //输出p的值
  p=p->next;
 }
 putchar('\n');
 return OK;
}
//(9)查找:从链表L中查找与指定元素x相同的结点,如果存在,返回该结点指针,如果不存在,返回NULL
LinkList SearchList_Link(LinkList L,ElemType x)
{
 LinkList p=L->next;
 while(p!=NULL)
 {
  if(p->data==x)
   break;
  p=p->next;
 }
 return p;
}
//(10)清空:将链表L清空,就是将L变成一个空表
//方法一:调用前面的Delete函数,每次删除表头结点(就是删除第一个结点),这样,删除了第一个结点后,原来的第二个结点就变成了新的第一个结点,以此类推,一直删除下去,直到链表为空:
Status ClearList_Link_1(LinkList &L)
{
 ElemType e;
 while(!ListEmpty_Link(L))  //一直删除,直到链表为空
  ListDelete_Link(L,1,e);  //删除第一个结点
 return OK;
}
//方法二:每次都删除第1个结点,直到链表为空
Status ClearList_Link_2(LinkList &L)
{
 LinkList p=L->next;  //p始终指向第一个结点,就是L后面的
 while(p!=NULL)
 {
  L->next=p->next;  //L->next绕过p,指向p后面的那个结点
  free(p);    //删除p
  p=L->next;    //p始终指向第一个结点,就是L后面的
 }
 return OK;
}
//(11)销毁:销毁链表L
Status DestroyList_Link(LinkList &L)
{
 ClearList_Link_1(L);  //先将链表清空
 free(L);     //然后再将头结点释放
 L=NULL;     //释放了的指针,通常应该让它指向NULL
 return OK;
}
//逆置单链表L
Status InverseList_Link(LinkList &L)
{
 LinkList M;
 M=(LinkList)malloc(sizeof(LNode)); //先生成一个头结点M
 M->next=NULL;
 LinkList p=L->next;
 while(p!=NULL)
 {
  L->next=p->next; //先让p从L中脱落,就是让L绕过p
  p->next=M->next; //将p结点向头生长插入到M的第一个结点
  M->next=p; 
  p=L->next;   //p又指向L中的第一个结点
 }
 free(L);
 L=M;
 return OK;
}


你可能感兴趣的:(笔记&记录,数据结构,链表,算法)