习题来自B站up:白话拆解数据结构
单链表结构体看这里https://blog.csdn.net/m0_61522765/article/details/141536639?spm=1001.2014.3001.5501
今日题目如下:
(1)将单链表就地逆置(补昨天的)
(2)删除带头结点的单链表L中的最小值。
(3)删除带头结点的单链表L中所有值为x的结点,并释放其内存空间。
题1
法一:可以采用昨天的头插法,首先将头结点摘下来,然后将后面的结点一个个头插到头结点后面。
Linklist list_reverstbyhead(Linklist &L){
Lnode *p,*r;
p=L->next;
L->next=NULL; // 摘下头结点
while(p){
r=p->next;
p->next=L->next;
L->next=p;
p=r;
}
return L;
}
实践一下:成功!
法二:三指针法,这个有点不好想,但其实也还好,可以自己动手画一下。首先需要这样的三个指针:
然后我们改变2的指针域,让2指向1,再把1的指针域置空(因为1结点是表尾结点):
记得将L的指针域也置空,因为L的指针域最后要指向新的表头结点;然后我们把p,q,r向后面移动,继续这样操作就行了!
// 法二:三指针法
Linklist three_zhizhen(Linklist &L){
Lnode *p,*q,*r=NULL;
p = L->next;
q = p->next;
L->next = NULL; //摘下头节点
p->next = NULL;
while(q!=NULL){
r=q->next;
q->next = p;
p=q;
q=r;
}
L->next= p;
return L;
}
注意r指针的赋值要在while循环里,他要后赋值,指向q的next,如果让r指针和后面俩指针一起移动,就会出现当q指向最后一个结点时,r已经指向空了,此时判断条件是q不指向空,所以还要往下走一轮,r再往下走就会报错!
实践一下:成功!
完整代码如下:(在vscode环境下已跑通)
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 单链表结构体定义
typedef struct Lnode{
int data;
Lnode *next;
}Lnode,*Linklist;
Linklist list_insertbytail(Linklist &L){
if (L == NULL || L->next == NULL) return L;
Lnode *s;
int x;
L = (Lnode*)malloc(sizeof(Lnode));
L->next = NULL;
Lnode *r = L;
cin >> x;
while(x!=9999){
s = (Lnode*)malloc(sizeof(Lnode));
s->data=x;
s->next=NULL;
r->next = s;
r=r->next;
cin >> x;
}
return L;
}
// 链表就地逆置
//法一:头插法
Linklist list_reverstbyhead(Linklist &L){
Lnode *p,*r;
p=L->next;
L->next=NULL;
while(p){
r=p->next;
p->next=L->next;
L->next=p;
p=r;
}
return L;
}
// 法二:三指针法
Linklist three_zhizhen(Linklist &L){
Lnode *p,*q,*r=NULL;
p = L->next;
q = p->next;
L->next = NULL; //摘下头节点
p->next = NULL;
while(q!=NULL){
r=q->next;
q->next = p;
p=q;
q=r;
}
L->next= p;
return L;
}
int main(){
Linklist L;
// list_insertbyhead(L);
list_insertbytail(L);
Lnode *p = L->next;
printf("origin list:");
while (p != NULL) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
printf("reverse list:");
list_reverstbyhead(L);
// three_zhizhen(L);
Lnode *q = L->next;
while (q != NULL) {
printf("%d-> ",q->data);
q = q->next;
}
printf("NULL");
return 0;
}
题2:
删除最小值,首先就要找到最小值,其实这个找最小值的过程和顺序表差不多,也是需要一个temp变量来记录,然后遍历比较,更新temp中的值。
Lnode *p,*q;
p=L;
q=L->next;
int temp=q->data;
while(q!=NULL){
if(q->data<temp)
{
temp = q->data;
p=p->next;
q=q->next;
}
p=p->next;
q=q->next;
}
只是找到了还不够,我们还要删除这个最小值结点,所以我们还需要两个指针变量,一个记录最小值结点的位置,一个记录最小值前驱的位置,因为删除操作需要知道结点的前驱。注意,这两个指针只在要更新temp值的时候更新指向,其他时候指向不变。
Linklist delete_min(Linklist &L){
if (L == NULL || L->next == NULL) return L;
Lnode *p,*q,*min_pre=NULL,*minp=NULL;
p=L;
q=L->next;
int temp=q->data;
while(q!=NULL){
if(q->data<temp)
{
min_pre = p; // 前驱
minp=q; // 最小值结点
temp = q->data;
p=p->next;
q=q->next;
}
p=p->next;
q=q->next;
}
// 删除最小值节点
if (minp == L->next) { // 如果最小值节点是第一个节点
L->next = minp->next;
} else {
min_pre->next = minp->next;
}
return L;
}
实践:成功删掉了最小值2.
完整代码如下:
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 单链表结构体定义
typedef struct Lnode{
int data;
Lnode *next;
}Lnode,*Linklist;
Linklist list_insertbytail(Linklist &L){
Lnode *s;
int x;
L = (Lnode*)malloc(sizeof(Lnode));
L->next = NULL;
Lnode *r = L;
cin >> x;
while(x!=9999){
s = (Lnode*)malloc(sizeof(Lnode));
s->data=x;
s->next=NULL;
r->next = s;
r=r->next;
cin >> x;
}
return L;
}
// 删除带头节点的单链表L中最小值(最小值唯一)
Linklist delete_min(Linklist &L){
if (L == NULL || L->next == NULL) return L;
Lnode *p,*q,*min_pre=NULL,*minp=NULL;
p=L;
q=L->next;
int temp=q->data;
while(q!=NULL){
if(q->data<temp)
{
min_pre = p;
minp=q;
temp = q->data;
p=p->next;
q=q->next;
}
p=p->next;
q=q->next;
}
// 删除最小值节点
if (minp == L->next) { // 如果最小值节点是第一个节点
L->next = minp->next;
} else {
min_pre->next = minp->next;
}
return L;
}
int main(){
Linklist L;
// list_insertbyhead(L);
list_insertbytail(L);
Lnode *p = L->next;
printf("origin list:");
while (p != NULL) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
delete_min(L);
printf("new list:");
Lnode *q = L->next;
while (q != NULL) {
printf("%d-> ",q->data);
q = q->next;
}
printf("NULL");
return 0;
}
题 3:
法一:需要两个指针,一个指向值为x结点的前驱,一个指向x结点;当遍历到结点data为x的时候,指向删除操作,不为x的时候,两个指针就向后移。
Linklist del_allx(Linklist &L,int x){
if (L == NULL) return L;
Lnode *p,*q;
p=L;
q=L->next;
while (q != NULL) {
if (q->data == x) {
// 删除节点 q
p->next = q->next;
free(q);
q = p->next; // 更新 q 为删除后的下一个节点
} else {
// 如果当前节点不需要删除,继续遍历
p = q;
q = q->next;
}
}
return L;
}
实践:x=3
法二:尾插的思想,将data不为x的结点重新尾插到L后面,可以当成新表来看待!
Linklist del_allx2(Linklist &L, int x) {
if (L == NULL) return L; // 如果链表为空,直接返回
Lnode *p = L;
Lnode *q = L->next;
while (q != NULL) {
if (q->data != x) {
p->next = q; // 当前节点不需要删除
p = p->next; // 更新 p
} else {
Lnode *temp = q; // 保存要删除的节点
q = q->next; // 更新 q
free(temp);
continue; // 跳过
}
q = q->next;
}
// 处理最后一个节点的 next,确保链表的末尾指向 NULL
p->next = NULL;
return L;
}
这里的continue不能省略,如果省略会造成q一次走两步,会出错!
实践:x=4
完整代码如下:
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 单链表结构体定义
typedef struct Lnode{
int data;
Lnode *next;
}Lnode,*Linklist;
Linklist list_insertbytail(Linklist &L){
Lnode *s;
int x;
L = (Lnode*)malloc(sizeof(Lnode));
L->next = NULL;
Lnode *r = L;
cin >> x;
while(x!=9999){
s = (Lnode*)malloc(sizeof(Lnode));
s->data=x;
s->next=NULL;
r->next = s;
r=r->next;
cin >> x;
}
return L;
}
//带头节点链表L中,删除所有值为x的节点并释放其内存空间(x不唯一)
Linklist del_allx(Linklist &L,int x){
if (L == NULL) return L;
Lnode *p,*q;
p=L;
q=L->next;
while (q != NULL) {
if (q->data == x) {
// 删除节点 q
p->next = q->next;
free(q);
q = p->next; // 更新 q 为删除后的下一个节点
} else {
// 如果当前节点不需要删除,继续遍历
p = q;
q = q->next;
}
}
return L;
}
//法二:将头节点断开,将不为x的元素重新接上去
Linklist del_allx2(Linklist &L, int x) {
if (L == NULL) return L; // 如果链表为空,直接返回
Lnode *p = L;
Lnode *q = L->next;
while (q != NULL) {
if (q->data != x) {
p->next = q; // 当前节点不需要删除
p = p->next; // 更新 p
} else {
Lnode *temp = q; // 保存要删除的节点
q = q->next; // 更新 q
free(temp);
continue; // 跳过
}
q = q->next;
}
// 处理最后一个节点的 next,确保链表的末尾指向 NULL
p->next = NULL;
return L;
}
int main(){
Linklist L;
// list_insertbyhead(L);
list_insertbytail(L);
Lnode *p = L->next;
printf("origin list:");
while (p != NULL) {
printf("%d ",p->data);
p = p->next;
}
printf("\n");
del_allx2(L,4);
printf("new list:");
Lnode *q = L->next;
while (q != NULL) {
printf("%d-> ",q->data);
q = q->next;
}
printf("NULL");
return 0;
}