线性表的链式存储
单链表的定义:
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
Typedef struct LNode{
ElemType data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
- 建立单链表
设节点的数据类型是整型动态建立单链表的方法有头插式和尾插式
头插式: 从一个空表开始,重复读入数据,生成新节点,将读入数据存放到新节点的数据域,然后将新节点插入到当前链表的头部,直到读入结束标记为止,每次插入的节点都作为链表的第一个节点。
头插节点算法图解:
算法实现:
LNode *createLinkedList(void) //链表节点头插式
{
int data;
LNode *head , *p; //创建头节点和节点p
head = (LNode *)malloc(sizeof(LNode)); //动态分配内存空间
head->next = NULL; //初始头指针指向空
while(1)
{
scanf(“%d”,&data);
if(data>=32767) break;
p = (LNode *)malloc(sizeof(LNode); //给节点p分配存储单元
p->data = data;
p->next = head->next; //将p节点的后继指向为原头指针的后继元素
head->next = p; //将头指针指向p节点
}
return (head); //返回这个链表
}
尾插式: 将新节点插入到当前链表的表尾,使其成为当前链表的尾节点。
链表节点尾插式算法描述:
LNode *createLinkedList(void) //链表节点头插式
{
int data;
LNode *head ,* last, *q; //创建头指针和尾指针和节点q
head = q = (LNode *)malloc(sizeof(LNode)); //动态分配内存空间
q->next = NULL; //创建单链表的表头节点head
while(1)
{
scanf(“%d”,&data);
if(data>=32767) break;
q = (LNode *)malloc(sizeof(LNode); //给节点q分配存储单元
q->data = data; //数据域赋值
q->next = last->next; //将q节点指向last原指向(可换作倒数第二行)
last->next = q; //尾节点指向插入的节点q
last = q; //插入的节点变成尾节点(后移一位)
}
//last->next = NULL;
return (head);
}
如果插入建立单线性链表的节点是n个,算法时间复杂度为O(n)。
链表的CRUD操作的实现:
按序号查找节点值:
LNode *LocateElem (LNode * L , int e)
{
int j =1; LNode *p;
*p = L->next;
while(p! = NULL && j<i){
p = p->next;
j++;
if( j != i)
return NULL;
else
return (p->data);
}
}
按值查找链表节点:
LNode *Locate_Node(LNode *L , int key)
{
LNode *p = L-next;
while(p!=NULL&&p->data!=key){
p = p->next;
if(p->data == key)
return p;
else
return NULL;
}
}
该算法时间复杂度与形参key值有关,平均时间复杂度为O(n)
- 单链表的插入:
插入运算是将值为e的新节点插入到表的第i个节点的位置上,即插入到ai-1与ai之间。所以必须首先找到ai-1所在的节点p,然后生成一个数据域为e的新节点q,q节点作为p的直接后继节点
单链表各种操作的具体实现:
#include<stdio.h>
//定义单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
} LNode, *LinkList;
LinkeList CreatList1(LinkList &L) //头插法建立单链表
{
LNode *s;
int x;
L = (LinkList *)malloc(sizeof(LNode)); //动态分配内存空间
L->next = NULL; //头节点指向空
scanf("%d",&x);
while(x!=9999)
{
s = (LNode *)malloc(sizeof(LNode));
s->data = x;
s->next = L->next;
L->next = s;
scanf("%d",&x);
}
return L;
}
LinkList *CreatList2(LinkList &L) //尾插法建立单链表
{
LNode *s; //在末尾准备插入的结点
LNode *r = L; //定义尾指针
int x;
L = (LinkList *)malloc(sizeof(LNode)); //动态分配内存空间
scanf("%d", &x);
while(x!=9999)
{
s = (LNode *)malloc(sizeof(LNode)); //最后插入的节点分配空间
s->data = x;
r->next = s; //在尾结点r的后面插入节点s
r = s; //尾结点的指针指向s节点
scanf("%d", &x);
}
r->next = NULL; //最后的节点一定要指向NULL
return L;
}
LNode *GetElem(LinkList L, int i) //按序号i查找元素
{
int j = 1;
LNode *p = L->next; //初始化指针p为头指针
if(i==0)
{
return L; //返回头节点
}
else if(i<1)
{
return NULL; //情况不存在
}
while(p&&j<i)
{
p = p->next; //指针p向后指向
j++; //计数器j往后移一位
}
return p; //返回当前节点
}
LNode *LocateElem(LinkList L, ElemType e) //按值查找节点
{
LNode *p = L->next;
while(p != NULL && p->data != e)
{
p = p->next; //链表往后移动查找
}
return p;
}
LNode *CountLength(LinkList L,int x) //求单链表的长度
{
LNode *p = L->next;
int i=0;
while(1)
{
p->next = p;
i++;
}
return i;
}
void main()
{
LinkList *p,*q,*s; //s是待插节点,再定义一个临时节点p
/****前插法插入节点****/
p = GetElem(L, i-1); //找到i-1位置的节点 p (待插节点的前驱结点)
s->next = p->next; // 将p的原指向转给s
p->next = s; //p指向s,即s在p节点(i-1)位置的后面 i的位置
/****后插法插入节点****/
p = GetElem(L, i); //找到i位置的节点 p (待插节点的位置)
s->next = p->next; //交换指向 ,s节点就到了p节点(i位置)的后面i+1的位置
p->next = s;
int temp; //用临时变量交换data值
temp = p->data; //实现i和i+1位置节点值的交换
p->data = s->data;
s->data = temp;
/****删除节点的操作****/
p = GetElem(L,i-1); //查找目标位置之前的节点p
q = p->next; //q是目标删除节点 ,在p节点后面
p->next = q->next; //断开链接 ,将q原指向直接由p节点指向
free(q); //释放节点的存储空间
/****删除节点的骚操作****/
p = GetElem(L,i-1);
q = p->next; //q指向*p节点的后继(i位置)
p->data = q->next->data; //将p的数据域和q节点的后继元素(i+1)的数据域交换
p->next = q->next; //指针q的指向交给了p的指向,实现了断链
free(q);
}