24考研数据结构-线性表4

2.4.4单链表的查找操作(默认带头节点,不带头节点后续更新)

2.4.4.1 按位查找操作

GetElem(L, i): 按位查找操作,获取表L中第i个位置的元素的值;

LNode * GetElem(LinkList L, int i){
    if(i<0) return NULL;
    
    LNode *p;               //指针p指向当前扫描到的结点
    int j=0;                //当前p指向的是第几个结点
    p = L;                  //L指向头结点,头结点是第0个结点(不存数据)
    while(p!=NULL && j<i){  //循环找到第i个结点
        p = p->next;
        j++;
    }

    return p;               //返回p指针指向的值
}


注意:
1.边界情况 i=0,返回头节点;i>L.length,返回null;
2.j<i即查找到j = i 的节点,就是第i个节点。
3.平均复杂度O(n)
2.4.4.2 按值查找操作

LocateElem(L, e):按值查找操作,在表L中查找具有给定关键字值的元素;
平均复杂度O(n)

LNode * LocateElem(LinkList L, ElemType e){
    LNode *P = L->next;    //p指向第一个结点
    //从第一个结点开始查找数据域为e的结点
    while(p!=NULL && p->data != e){
        p = p->next;
    }
    return p;           //找到后返回该结点指针,否则返回NULL
}

2.4.4.3 求单链表的长度(带和不带头节点都写了)

Length(LinkList L) :计算单链表中数据结点**(不含头结点)的个数**,需要从第一个结点看是顺序依次访问表中的每个结点。算法的时间复杂度为O(n)

带头节点:

int Length(LinkList L){
    int len=0;       //统计表长
    LNode *p = L;
    while(p->next != NULL){  //只有指向的下一个节点不为null,才len++
        p = p->next;
        len++;
    }
    return len;
}


不带头节点:

int Length(LinkList L){
    int len=0;       //统计表长
    LNode *p = L;
    while(p!= NULL){  //当前指针(即头节点指向的第一个节点)不为空即可++,
    //带头节点的链表用这种方法长度会算上头节点。
        p = p->next;
        len++;
    }
    return len;
}


2.4.4.4 知识回顾与重要考点

在这里插入图片描述

2.4.5 单链表的创建操作

2.4.5.1 头插法建立单链表

带头节点;
若不带头节点,头插法就是插入头指针指向的第一个节点
平均时间复杂度O(n)
思路:每次都将生成的结点插入到链表的表头。

LinkList List_HeadInsert(LinkList &L){       //逆向建立单链表
    LNode *s;
    int x;
    L = (LinkList)malloc(sizeof(LNode));     //建立头结点
    L->next = NULL;                          //初始为空链表,这步不能少!

    scanf("%d", &x);                         //输入要插入的结点的值
    while(x!=9999){                          //输入9999表结束
        s = (LNode *)malloc(sizeof(LNode));  //创建新结点
        s->data = x;
        s->next = L->next;
        L->next = s;                         //将新结点插入表中,L为头指针
        scanf("%d", &x);   
    }
    return L;
   
}


在这里插入图片描述

2.4.5.2 尾插法建立单链表

带头节点;
若不带头节点则需要特殊处理第一次插入数据的情况,是直接赋值而不是对下一个节点赋值。
时间复杂度O(n)

思路:每次将新节点插入到当前链表的表尾,所以必须增加一个尾指针r,使其始终指向当前链表的尾结点。
好处:生成的链表中结点的次序和输入数据的顺序会一致。

LinkList List_TailInsert(LinkList &L){       //正向建立单链表
    int x;                                   //设ElemType为整型int
    L = (LinkList)malloc(sizeof(LNode));     //建立头结点(初始化空表)
    LNode *s, *r = L;                        //r为表尾指针
    scanf("%d", &x);                         //输入要插入的结点的值
    while(x!=9999){                          //输入9999表结束
        s = (LNode *)malloc(sizeof(LNode));
        s->data = x;
        r->next = s;
        r = s                                //r指针指向新的表尾结点
        scanf("%d", &x);   
    }
    r->next = NULL;                          //尾结点指针置空
    return L;
}



在这里插入图片描述

注意:
头插法和尾插法在初始化的时候
头插法:
    L = (LinkList)malloc(sizeof(LNode));     //建立头结点
    L->next = NULL;                          //初始为空链表,这步不能少!
尾插法:
	L = (LinkList)malloc(sizeof(LNode));     //建立头结点(初始化空表)
	r->next = NULL;                          //尾结点指针置空
都是为了保证最后一个节点指向的不是脏数据,即malloc动态分配空间的时候可能,
指向的是一个脏数据

在这里插入图片描述

在这里插入图片描述

2.4.5.3 链表的逆置

算法思想:逆置链表初始为空,原表中结点从原链表中依次“删除”,再逐个插入逆置链表的表头(即“头插”到逆置链表中),使它成为逆置链表的“新”的第一个结点,如此循环,直至原链表为空;

带头节点:

void listReverse(linkedList &L)
{
	node *p,*s;
	//1.准备工作
	p = L->next;
	L->next = NULL;
	
	while(p)
	{
		//2.1 s记录正在处理的结点,p记录下一轮待处理的结点
		s = p; 			//s承接上一轮记录的位置
		p = p->next; 	//p为下一轮记录位置
		//2.2 把s插入 已逆置的部分 中
		s->next = L->next;  // L->next代表已逆置的第一结点,s的指针域指向它
		L->next = s;	//(头结点的指针域,即)第一结点 设置为s
		//2.2步骤相当于:
		//s 对 队伍(已逆置部分)的队首(已逆置的第一结点)说:你不要排在柜台前了,你排在我后面
		//等队伍排在s后面后,s自己排到了柜台前
	}
}

讲解
我们先看第一轮循环做了什么:

阅读顺序:黑色(初始)、蓝色(操作)、红色(理解)

在这里插入图片描述

第二轮:

阅读顺序:黑色(初始)、蓝色(操作)、红色(理解)

在这里插入图片描述
总结
不难发现:

  1. 链表逆置利用了s、p两个指针的移动实现
    每一轮循环体执行结束后,s指向刚刚逆置成功的结点,p指向下一轮待逆置的结点

  2. 为什么需要p?
    因为2.2步骤中s->next会被改写,
    若只有s,会丢失剩余的结点,
    这时候p起到暂存的作用,等待下一轮2.1步骤中的s=p找到它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VengaZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值