单链表课后题:
//递归删除指定元素
//单链表第一题
void DeletX(Link* (&k), int delnum) {
//第一个函数必须为引用值防止断链,就相当于传入的是k-》next
struct Link* tmp;
if (k == NULL) return;//边界条件
if (k->next == delnum) {
//删除其元素并且继续向里递归
tmp = k;
k = k->next;
free(tmp);
DeletX(k, delnum);
}
else {
//不等于
//也是向内递归
DeletX(k->next, DeletX);
}
第二题
//带头结点的链表删除指定值
void DelX(Link* p, int delnum) {
struct Link* tmp=p;
struct Link* fir = p->next;
struct Link* tmp2;
while (fir)
{
if (fir->value==delnum) {
tmp2 = fir;//保存删除结点的地址
fir = fir->next;//指向下一个结点
tmp->next = fir;//删除节点的前一个结点指向新的结点
free(tmp2);
}
else {
//若不相等则继续遍历
tmp = fir;
fir = fir->next;//保持tmp一直指向fir前一个结点即可
}
}
}
//第三题
//反向输出链表所有元素
//实际上就是把所有元素放入栈中最后在一起取出来即可
void ReserveOutPut(Link* p) {
if (p == NULL) return;//边界条件:直到没有链表指针后依次出栈
ReserveOutPut(p->next);
printf("%d", p->next);
}
//第四题
//第四题函数
//带头结点所以第一步就是保存头节点以及保留下下个结点
//头->带数据结点->下一个结点->下下个结点
//删除下一个结点所以要保留下下个结点
void DeletMin(Link* p) {
struct Link* fir = p;//记录头结点
struct Link* minPin = p->next;
struct Link* prePin = p->next;
struct Link* next = p->next->next;//记录下下个结点
while (next) {
if (next->value < minPin->value) {
//如果比当前值要小
minPin = next;//记录当前最小值节点
fir = prePin;//前驱也要更换
}
//否则继续前进
//要一直记录前驱
prePin = next;
next = next->next;
}
//这时候已经找到最小值啦
struct Link* f = minPin;
fir->next = minPin->next;
free(f);
}
第五题单链表经典题目
相似题目(有道原题)
//就地逆置单链表(带头结点)
//其实很简单的方式就是让指针指向的方向逆置
void Reverse(Link* p) {
struct Link* pre = p;//记录头节点
struct Link* rec = p->next;//记录
struct Link* q = p->next;
struct Link* tmp;
while (rec) {
tmp = rec->next;//记录下一个结点
rec->next = pre;//指向数据结点前一个结点也就是换方向
pre = rec;//前一个结点往下移一位
rec = tmp;//当前结点也往下移动
}
//还完之后在处理第一个结点和最后一个结点
p->next = pre;//为什么是pre因为他保存是rec的上一个结点rec寄了自然是pre
//还记得第一个数据结点码,处理它
q->next = NULL;
}
第六题涉及到排序算法打算等到最后一章回来再写
//待更新。。。
第七题:带头结点无序单链表中删除表中所有介于给定的两个值之间的元素的元素(easy~)
void DelNum(Link* L, int min, int max) {
struct Link* pre = L;
struct Link* p = pre->next;
struct Link* tmp;
while (p) {
//依次判断是否在其范围之内
if (p->value > min && p->value < max) {
//如果满足删除
tmp = p->next;//记录当前结点的下一个结点
pre->next = p->next;
free(p);//删除当前结点
p = tmp;
}
//若不在继续往下走
pre = p;
p = p->next;
}
}
第八题太简单懒得写了hh
第九题按递增次序输出单链表中各节点的数据元素,并且释放结点所占的存储空间
void PrintandDel(Link* L) {
//我就懒得改我设定的adt了num=value
struct Link* pre = L;
struct Link* p = L->next;
struct Link* tmp;//上面三个常用管他要不要不要再删
struct Link* min = L->next;//用于记录最小值结点
struct Link* minPre = L;//记录最小值前一个结点
int count = 0;
while (p) {
count++;
p = p->next;
}
p = L->next;
for (int i = 0; i < count; i++) {
//依次找出最小的然后删除
//有点暴力但是简单
while (p) {
if (p->value < min->value) {
//如果小换
minPre = pre;
min = p;
}
pre = p;
p = p->next;
}
//这时候最小的已经出现了输出删除即可
printf("%d", min->value);
tmp = min;
minPre->next = min->next;
free(tmp);
//重置
pre = minPre=L;
p = min = L->next;
}
printf("\n");
return;
}
第十题:讲带一个头结点的单链表A分解为两个带头结点的单链表A和B,使得a表中含有原表中序号为奇数的元素,b表中含有原表中序号为偶素的元素,且保持相对顺序不变
void Divide(Link* la, Link* lb) {
int flag = 0;//用于奇偶校验
struct Link* l = la;
struct Link* p = la->next;
struct Link* a = la;
struct Link* b = lb;
l->next = NULL;//原链表的头节点设为空
while (p) {
//使其顺序不变采取尾插法
if (!flag) {
//0时为奇数
a->next = p;
a = p;
flag = 1;
}
if (flag) {
//为偶
b->next = p;
b = p;
flag = 0;
}
p = p->next;
}
//边界处理
a->next = NULL;
b->next = NULL;
}
第十一题和第十题差不多哦也是拆分成两个顺序表
唯一个要注意的时b表采取尾插法因为是逆序的
//如果是奇数不动偶数提出来
//b中采取头插法
void DivideList(Link* L) {
struct Link* l2 = (Link*)malloc(sizeof(Link));
l2->next = NULL;
Link* p = L->next, * q, * r = L;
while (p)
{
r->next = p;
r = p;
p = p->next;
if (p) q = p->next;
p->next = l2->next;
l2->next = p;
p = q;
}
r->next = NULL;
}
第十二题:在一个递增有序的线性表中,有数值相同的元素存在,若存储方式为单链表,设计算法去掉数值相同的元素,是表中不再有重复的元素。。
双指针,两个向前进,然后相同去掉
//12
void deleteRep(Link* L) {
struct Link* pre = L, * p = L->next, * r;//删除无脑写(主要是防止断链)
while (p->next) {
if (p->value == p->next->value) {
r = p->next;
pre->next = p->next;
free(p);
p = r;
}
else
{
//不相等
pre = p;
p = p->next;
}
}
}
第十三题:假设有两个按元素值递增有序的线性表,均已单链表的形式春促,请编写算法使得这两个的那链表归并成为一个按元素之递减次序排序的单链表,并且要求利用原来两个单链表的结点存放归并后的单链表
因为是要递减顺序所以这里采取头插法将小的值依次放入
void MergeList(Link* la, Link* lb) {
struct Link* l = (Link*)malloc(sizeof(Link)), * pa = la->next, * pb = lb->next, * ra, * rb;
l->next = NULL;
//采取头插法的方式
while (pa && pb) {
if (pa->value < pb->value) {
//也就是要头插法插入pa
ra = pa->next;//保留下一个结点
pa->next = l->next;
l->next = pa;
pa = ra;
}
else {
rb = pb->next;
pb->next = l->next;
l->next = pb;
pb = rb;
}
}
while (pa) {
ra = pa->next;
pa->next = l->next;
l->next = pa;
pa = ra;
}
while (pb) {
rb = pb->next;
pb->next = l->next;
l->next = pb;
pb = rb;
}
}
第十四题:设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的公共元素残生单链表c,要求不破坏A。B结点
//14
void LinkCom(Link* a, Link* b, Link* c) {
struct Link* lc = c, * pa = a->next, * pb = b->next,*rc=lc;
while (pa) {
//没遍历一个la就遍历依次lb找到相同放入c
while(pb){
if (pa->value == pb->value) {
struct Link* q = (struct Link*)malloc(sizeof(struct Link));
q->value = pa->value;
rc->next = q;
rc = q;
break;
}
pb = pb->next;
}
pa = pa->next;
pb = b->next;
}
rc->next = NULL;
}
第十五题:已知两个链表A和B分别表示两个集合,其元素递增排列,编制函数,求A与B的交集并存放在A链表中
思路:
用la记录A的头节点,然后数据节点用pa指示然后再用pb指针指向B的首个数据结点然后两个papb所指向的数据放一起比较即可
//第十五题具体实现
void ListSame(Link* a, Link* b) {
struct Link* pa = a->next, * pb = b->next, * r, * la = a;//r防止断链
la->next = NULL;//新链接个链表
while (pa && pb) {
if (pa->data == pb->data) {
//一样就要拿出来放在la后面
la->next = pa;
la = pa;
pa = pa->next;
pb = pb->next;
}
else {
//不一样由于是递增排序,看哪边小那边往后移动
pa->data < pb->next ? pa = pa->next; pb = pb->next;
}
}
//放完了
la->next = NULL;
}
第十六题:两个连续序列 A=a1,a2,a3,...am
和 B=b1,b2,b3,..bn
已经存入两个单链表中,设计一个算法,判断b是否是a中的连续子序列
可以用滑窗但是他没要求时间复杂度,直接暴力就行亦不容易错
//第十六题
void SubList(Link* a, Link* b) {
struct Link* la = a, * pa = a->next, * pb = b->next;
while (pa && pb) {
if (pa->data == pb->data) {
//如果相等继续比对下一个
pa = pa->next;
pb = pb->next;
}
else {
//不相等就重新来一次
pb = b->next;//pb从头来一次
la = la->next;//说明前面没有符合的了往后移动一个结点
pa = la->next;//pa和la本来之间就差一个所以pa继续往后移一位这很合理
}
}
pb == NULL ? printf("yes,sir!") : printf("fuck,they dont match!");
}
这里有循环双链表的题目
第十七题:设计一个算法用于判断带头结点得循环双链表是否对称
考试真的会出双链表嘛。。。
如果时判断是否循环得话就头结点得前面
pre
指向得值一定是等于next
的
//首先是创建一个循环双链表
typedef struct Link {
int value;
struct Link* pre;
struct Link* next;
}Link;
struct Link* CreateDouLink() {
//创建头节点
struct Link* head = (struct Link*)malloc(sizeof(struct Link));
head->next = head->pre = NULL;
struct Link* p = head;
printf("请输入节点个数");
int count;
int value;//记录值
scanf("%d", &count);
for (int i = 0; i < count; ++i) {
printf("请输入第%d个节点的值", i+1);
scanf("%d", &value);
struct Link* newP = (struct Link*)malloc(sizeof(struct Link));
newP->value = value;
newP->pre = p;
p->next = newP;
p = newP;
}
p->next = head;
head->pre = p;
return head;
}
然后再写写判断函数
void IsSymmetric(Link* l) {
struct Link* pre = l->pre, * next = l->next;
while (pre != next && pre->pre != next) {
//注意偶数个结点和奇数个结点对称位置不一样
//奇数个结点就是当他们一样的时候退出
//偶数个结点就是去掉头节点一边肯定多一个结点再向前一个区判断
if (pre->value != next->value) {
printf("不对称!一点也不symmetric");
break;
}
else {
//说明相等继续向前比对
//卧槽冰,狂风向前送是吧
pre = pre->pre;
next = next->next;
}
}
//退出循环时
if (pre->value == next->value || pre->pre->value == next->value) {
//首尾呼应芜湖
printf("该循环双链表symmetric");//就很洋气
}
}
这里是循环单链表的题目
第十八题:有两个循环双链表,链表头指针分别为h1
和h2
,编写一个函数将链表h2
链接到链表h1
之后,要求连接后的链表仍然保持循环链表的形式。
h2的最后一个结点连接到h1的头节点上来不就行了easy
//创建循环单链表的函数
typedef struct Link {
int value;
struct Link* next;
}Link;
Link* CreateSingleLoopLink() {
struct Link* head = (struct Link*)malloc(sizeof(struct Link));//创建头节点
head->next = NULL;
struct Link* p = head;
printf("请输入结点数");
int count,data;
scanf("%d", &count);
for (int i = 0; i < count; ++i) {
printf("这是第%d个结点", i+1);
scanf("%d", &data);
struct Link* newP = (struct Link*)malloc(sizeof(Link));
newP->value = data;
p->next = newP;
p = newP;
}
p->next = head;
return head;
}
然后解题
void ConnectTwoList(Link* h1, Link* h2) {
struct Link* p1 = h1->next, * p2 = h2->next;
while (p1->next != h1)
p1 = p1->next;//找到尾结点
p1->next = p2;
//接下里找到p2的尾结点链接p1
while (p2->next != h2)
p2 = p2->next;
p2->next = h1;
free(h2);//不需要h2这个头节点了
}
第十九题:带有头节点的循环单链表,其结点值均为正整数。设计一个算法,反复找出单链表中结点值最小的结点并且输出,最后将该节点从中删除,直到单链表空为止,在删除表头结点
关键字:反复删除,找最小,输出
void inputAndDeleteLink(Link* L) {
struct Link* pre = L, * minpre = L, * p = L->next, * min = L->next, * r;
while (L->next = L) {
//如果头节点后面还有值就说明未结束
while (p != L) {
//寻找最小值
if (min->value > p->value) {
min = pre;
minpre = pre;
}
//继续往下找
pre = p;
p = p->next;
}
//出循环已经找到最小值了
printf("%d", min->value);
//删除最小值结点
r = min->next;
minpre->next = min->next;
free(min);
p = L->next;//再次从头遍历
pre = L;
minpre = L;
min = L->next;
}
free(L);
}
第二十题:
主要思路:
其实就是一个查找加排序的过程,遍历找到对应结点freq加再放到前面去
定义结构体
typedef struct UnloopLink {
int value;
struct UnloopLink* next;
struct UnloopLink* pre;
int freq;//记录频度(frequency)
}UnloopLink;
创建非循环双向链表
和之前那个差不多就不写了
我的题解:
void Locate(UnloopLink* L, int num) {
struct UnloopLink* pre = L, * p = L->next, * preQ = L, * tmp,*q;
int flag;//设置标志位
while (p) {
if (p->value == num) {
//说明找到
flag = 1;
//频度上升
p->freq++;
tmp = p;//把该节点记录下来
//这时候要把该节点取出因为频度变了
if (p->next) {
//以防越界
pre->next = p->next;
p->next->pre = pre;
}
else {
//则说明后面为空
//直接丢弃即可
pre->next = NULL;
}
q = L->next;//q始终指向头节点的下一个结点
while (q) {
//进行排序
//也就是说把刚刚加了频度的点重新放入新的队列中
if (tmp->freq >= q->freq) {
//找到当前结点大于当前遍历结点直接插入
tmp->next = q;
q->pre = tmp;
preQ->next = tmp;
tmp->pre = preQ;
break;
}
//没有大于当前继续往下找
preQ = q;
q = q->next;
}
break;//完成任务退出循环
}
if (!flag) {
//说明未找到
printf("there are not the num which you wish");
}
}
}
真题待我第三轮再写。。