数据结构与算法整理2——链表的操作(C语言)
目录
1、线性表与链式表
1.1 线性表的概念:线性表是最简单,最常见,最基本的一种线性结构(数据的逻辑结构的一种),元素之间为线性关系,即除了第一个和最后一个元素之外,所有的元素都有前驱和后继元素,同一个线性表中的数据类型相同。
当数据元素为0时,可以是空表,但是没有空图的说法。
1.2线性表的特点:插入删除算法的时间复杂度为O(n),不适合多次插入或删除。
1.3 线性表的存储方式:
(1)顺序存储(又称顺序表)物理地址相连
a0 | A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8 | A9 |
(2)链式存储(又称链式表)
数据域 | 指针域 |
1.4 链式表的特点:
(1)可动态分配空间,插入删除算法的时间复杂度为O(1)。
(2)单链表在特定节点插入和删除元素之前需要遍历链表,所以算法的时间复杂度为O(n)。
(3)对于单链表的求表长,取元素,插入删除,释放掉几个操作的平均时间复杂度都是O(n)。
(4)但是链表只需比较元素不用移动元素,所以时间效率上优于顺序表。
2、顺序存储时插入、删除(移动元素的个数)
(1)定义顺序表的数据类型
typedef struct{
Int list[maxsize];
Int size;
}
(2)初始化
void listinitial(sequence *L){
L->size=0;
}
(3)插入元素:在i位置前插入一个新的元素,原顺序表中i位置之后的元素都要移动,并修改L->size+1;
插入算法的时间效率:平均移动元素的次数看做n/2(即移动一半元素),时间复杂度为O(n)
int insert(sequence *L, int i, int x){
int j;
if(L->size)>=mavsize{printf(“线性表已满”)}
else if (i<0||i>L->size){
printf(“参数不合法”)
}
else {
for(j=L->size;j>i;i++) //j指向顺序表最后一个元素size-1的后面
{L->list[j]=list[j-1];} //先逐个移动元素
L->list[i]=x; //再插入新的元素
L->size++; //使得线性表的总长度+1
}
}
(4)删除元素:删除i位置上的元素,将i后面的元素依次往前移,再令L->size-1
删除算法的时间效率:任意删除一个元素平均需要移动的次数看做n/2(即移动一半元素)时间复杂度为O(n)
int insert(sequence *L, int i , int x){
int j;
if(L->size)< =0{printf(“线性表为空,无法删除”)}
else if (i<0||i>L->size){ printf(“参数不合法”)}
else {
*x=L->list[i];
for(j=i+1;j<L->size-1;i++) // j指向要删除的元素的后一位
{L->list[j-1]=list[j];} //再将i后面的元素逐个前移
L->size--;
}}
(5)取元素 *x=L->list[i];
3、链式存储的插入,删除的语句如何写,如何判空
(1)定义链式表:
typedef struct Node
{
int data; //这样定义就是定义了一个数组 相当于int data[]
struct Node *next;
} Lnode,*LinkList; //Lnode等价于struct node定义了一个新的数据类型
//Lnode *L等价于 LinkList L定义一个指向节点的指针变量
(2)插入元素
Lnode *q=(Lnode *)malloc (sizeof(Lnode)) ; //分配一个新的节点空间
q->data=ele; //是该结点的数据域赋值为ele
q->next=temp->next; //把插入的节点的指针域指向下一个节点的data域
temp->next=q; //然后让要插入的节点的前一个节点的指针域指向该插入节点的数据域,两
//者的顺序不能乱,否则的话就丢失了插入节点的后一个节点的指针,找不到他了
(3)删除元素
Lnode * del=temp->next; //单独设置一个指针指向被删除结点以防丢失
temp->next=temp->next->next; //删除del的方法就是更改del-1的指针域,让他指向del+1的数据域,
//一步成功
free(del); //释放删除的节点del的空间
(4)单链表判空
1、不带头结点的单链表first为空的判定条件:first =NULL;
2、带头结点的单链表first为空的判定条件:first ->= NULL;
4、顺序存储与链式存储的优缺点
- 顺序表的存储优点:方法简单,容易实现;不用为表示节点间的逻辑关系而额外增加储存开销;具有按元素序号随机访问的特点
- 顺序表的存储缺点:对于n较大的顺序表,插入和删除元素需要移动的次数较大,效率低;需要预先分配足够大的空间,可能会导致大量闲置空间或者出现数据溢出;
- 链式表的优缺点与顺序表相反。
- 在选择存储方式时,若n较大,多进行插入删除操作,建议用链式存储,若按序号访问元素时,建议按顺序表存储(时间效率为O(1))。
5、相关几个例题
6、链表的操作代码(C语言)
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
char data; //这样定义就是定义了一个数组 相当于char data[]
struct Node *next;
} Lnode,*LinkList; //Lnode等价于struct node 定义了一个新的数据类型,Lnode *L等价于 LinkList L定义一个指向节点的指针变量
Lnode * ListCreat() //创建一个链表,函数类型是Lnode型,前面用Lnode * +name或者LinkList +name
{
Lnode *p=(Lnode*)malloc(sizeof(Lnode)) ; //创建一个头指针P指向头结点 malloc函数实在动态存储区 (堆)中分配一定大小的空间(sizeof(Lnode)然后返回该空间的头指针的起始值 一定要赋值给指针变量)此句中P指向头结点的data域
Lnode *temp=p; //此处 Lnode *temp=p 意思是temp这个指向节点的指针指向p创建的头结点的data域 ,使用中间变量指针 temp的目的是为了保证头指针 p 一直指向头结点,而不要变化
int i;
for(i=0;i<5;i++) //此处进行循环赋值
{
Lnode *a=(Lnode*)malloc(sizeof(Lnode)) ; //创建下一个节点,使指针a指向该节点的data域
a->data=i; //用a->data来赋值新创建的节点的data域的值为i
a->next=NULL; //将新创建的节点的 next指针域赋值为NULL
temp->next=a; //使之前创建的temp指针的temp->next(即头结点的指针域)中存放指针a的值(也就是a->data中data域的地址),所以temp-》next指向a->data 这样就把头结点的指针域指向了新创建节点的数据域
temp=temp->next; //再讲temp->next的值(也就是刚刚新创建的节点的数据域的地址)赋给temp是temp继续向下滑,为下一次循环赋值做准备
}
return p; //返回头指针;
}
void display(Lnode *p) //链表的输出函数 ,将头指针传入进来
{
Lnode *temp=p; //将头指针 赋值给temp,是的temp指向头节点
while(temp->next ) //如果temp-.next不是NULL就一直输出,使用while函数
{
temp=temp->next; //遍历
printf("%d",temp->data); //输出data值
}
printf("\n");
}
Lnode * InsertList(LinkList p,int ele,int num) //链表的插入函数,传入链表的头指针P,要插入的元素ele和插入的位置 num
{
Lnode *temp=p; //同理让temp指针指向头结点进行遍历找到要插入的位置 num
int i;
for(i=0;i<num;i++)
{
if(temp==NULL)
{
printf("插入位置无效");
}
temp=temp->next; //遍历
}
Lnode *q=(Lnode *)malloc (sizeof(Lnode)) ; //分配一个新的节点空间
q->data=ele; //是该结点的指针域赋值为ele
q->next=temp->next; //首先要赋值,把要插入的节点的指针域指向下一个节点的data域
temp->next=q; //然后让要插入的节点的前一个节点的指针域指向该插入节点的数据域,两者的顺序不能乱,否则的话就丢失了插入节点的后一个节点的指针,摘不到他了
return p;
}
Lnode * deleteele(LinkList *p ,int num) //删除链表第nun位置上的数字
{
Lnode * temp=p; // //将头指针赋值给temp,是的temp指向头节点
int i;
for(i=0;i<num;i++)
{
temp=temp->next; //遍历 链表找到要删除的位置num
}
Lnode * del=temp->next; //单独设置一个指针指向被删除结点,以防丢失
temp->next=temp->next->next; //删除某个结点的方法就是更改前一个结点的指针域
free(del); //释放删除的节点的空间
return p;
}
int main()
{
Lnode *p=ListCreat(); //首先要申明一个指向节点的指针p,在为他初始化
display(p);
p= InsertList( p,100,2); //再该链表的第2号位置插入数值100
display(p);
p=deleteele( p,3);
display(p); //传入的参数是头指针
return 0;
}
运行结果显示为: