王道数据结构精选习题及解析

顺序表

暴力法的时间复杂度为O(n²)

不要忽略有序性

思路:因为是有序的顺序表,所以重复的元素一定是连在一起的。那我们就使用两个指针,一个指针指向当前不重复有序表的最后一个元素,另一个会从头到尾遍历整个有序表,称为工作指针。

我们让工作指针往后移,如果与当前有序表最后一个元素相同,则工作指针需要继续后移,直到出现一个不重复的元素。然后我们将该元素放在当前有序表的后一个位置即可。代码如下:

重点在于使用双指针

经典归并

链表

基础知识

前驱英文:prior

静态链表:

这题很难,为什么不会断链呢?

事实上是因假如当前节点无需删除,那就将L的下一个结点传进去,并且传的是引用!!!因为我传的是引用,所以当我在随后调用的递归子函数中,如果该节点是x我们就要删除,怎么删除呢?我们只需要直接将L往后移一个位置!!

因为此时是引用,在子函数中我看似是只把自己往后移动了一位,但是这个子链表也相当于缩短了一截。而另一方面,对于父函数来说,父函数传给子函数的是引用:

子函数将自己后移,做的事情是把父亲的L->next也往后移动了一个位置!!!

删除结点时不要忘记要使用free将其free掉哦

顺带补充一下递归函数通过递归的方式实现正向输出的方法

反向输出的代码如下:

注意不要忘记,调用下一个结点时要考虑其是否为空才行

反转链表:

反转链表,比如需要将第一个结点指向第二个节点的方向逆转,让当前即cur结点指向前一个结点。

但是如果直接逆转了,就少了往后面结点走的路,需要一个next结点保留后续的结点。

所以需要多一个结点存储后续的next结点。

所以初始时:

让cur指向第一个结点,让next指向第二个节点,然后让pre指向空处

首先将当前结点反转,让其指向pre。

做完上述操作后,然后此时我们不要着急让next结点往回指,如果往回指了则next结点往后的路又断掉了。而且我们要保持一轮循环只做一次事。

当结点修改完毕后,我们让所有的三个结点往后移动一个位置。

最终代码如下:

有两个细节点需要注意!一个是要对初始结点进行判空!

还有就是最后一步,循环完毕时,最后一个结点是没有指向前一个结点的,因此不能忘记这一步操作!!

(暂时不会,先跳)

链表很多操作都不方便,但是插入操作比较方便,于是可以考虑用插入排序实现。

链表有一个头结点,我们初始化时也为其保留一个结点,也就是图上的a1,然后我们依次遍历a2及其后面的结点,将其在合适的位置插入

初始化工作:Listnode* p=head->next->next; 为a2赋值p

Listnode *r=p->next; 为a3赋值r

head->next->next=nullptr;即让a1指向nullptr

随后我们在已有的链表L往后遍历,找到第一个比a2大的数,然后在其前一个结点插入即可

找第一个比a2大的数通过while循环实现,while(结点的数都比a2小)则继续往后,

由于插入要在前一个结点插入,因此比较数据时是用当前结点的后面一个结点比较

这样就会在比a2大的结点的前一个地方停下来。

所以循环条件如下:

 

当这个循环完毕时

接下来进行插入操作即可

插入操作即:

插入操作完毕后继续遍历未找完的结点,即让p和r都往后走:

p=r

r=r->next

当找完了所有未排序的结点,即p为空时结束,故完整代码如下:

注意一点:创建链表插入结点使用尾插法时,不要忘记在结尾插入节点后,要将结尾重新调整为尾部!

并且还要注意一点,虽然链表B是新建了一个结点没错,但是对于链表A我们不是新建,而是在原来的链表基础上直接进行修改!

对于这种题我们需要使用三个指针,一个是ra、rb指针,分别代表A链表和B链表的尾部,每次在尾部插入结点,插入后再将ra、rb往后移动。

还有一个指针是工作指针,负责每次往后移动一位代表当前指针。

最后不要忘记要把a和b的指针置空哦

采用尾插法

本题所需注意细节点较多!是个重要的题!

头插法与尾插法不同在于,尾插法很简单,只需要让尾结点指向该节点即可

但是头插法较为麻烦,需要让该当前工作结点指向B表中已有的结点

普通尾插法直接后移即p=p->next可,但使用头插法需要会改变当前结点的后继结点,因此此时需要提前将p->next用q储存起来,然后工作节点p后移的操作改为p=q

其实就是设定两个指针,分别指向两个链表,然后每次将两个结点中较小的值选出来,以递增的顺序找出节点即可。

然后使用头插法就可以使其变为逆序,即降序排列了。

由于我们没有新建一个链表,而是在原来的l1的基础上去改箭头,所以我们需要一个多的指针去存储l1当前工作指针的下一个指针。

即每次循环前可以设定代码:

r =p->next

(首先需要让l1头置空,然后把工作指针p1放在l1头节点的后面那个有用的结点即值为的结点。

如图所示:

接下来我们比较两个工作指针p1和p2的值,假如p1的值小,那么我们就要把1插入到l1的头部的第一个位置。

所以:我们将p1的next指向l1的后续结点,再让l1的next指向p1:如图所示

P1->next=l1->next

L1->next=p1

然后这样p1就结束了他的工作,此时p1结点已经和上述链表断链,所以使用p=r赋值来获取新的工作结点。

并让r也后移一位

代码:p1=r  ,r=r->next

(如果是用l2的数据怎么办?其实此时r既可以暂存第一个工作指针的后续指针,也可以暂存第二个工作指针的后续指针,或者可以各有一个指针来缓存)

然后由于链表不等长,所以当某个链表为空后,继续将该链表的剩余结点依次插入即可。

通过这一行if(pa)pb=pa

的代码可以将pa设定为剩下的那一条链表

然后接下来依次插入即可

由于需要循环完一遍才知道哪个数据最小

而查找也需要从前往后查找一次,如果按照我原本的思想先找最小的,然后再从头遍历去查找该节点所在位置就很麻烦。

所以可以中途把最小值所在的结点也记录下来,这样就无需再查找一次。

此外为了删除该节点,需要我们不止要记录该最小值结点,还要记录最小值结点的前置结点,即minPre。

而为了记录最小值结点的前置结点,我们还需要一个结点,专门作为当前工作结点的前一个结点,工作结点后移一步的过程中,要保持该前置结点是工作结点的前一个结点。

猜你喜欢

转载自blog.csdn.net/weixin_43757333/article/details/129751970
今日推荐