链表c语言代码题库排坐标,链表的各种题目整理(C语言实现)

typedef struct node {

char *data;

struct node *next;

} node_t;

我们约定一个打印链表的函数:

void list_display(node_t *head)

{

for (; head; head = head->next)

printf("%s ", head->data);

printf("\n");

}

下面是几个常见的链表笔面问题:

1.计算链表长度

很简单:(复杂度O(n))

int list_len(node_t *head)

{

int i;

for (i = 0; head; head = head->next, i++);

return i;

}

测试:

int main(int argc, const char *argv[])

{

node_t d = {"d", 0}, c = {"c", &d}, b = {"b", &c}, a = {"a", &b};

printf("%d\n", list_len(&a));//4

return 0;

}

2.反转链表

我们多用几个指针就可以在O(n)完成反转任务:

算法:t遍历链表, q记录t的上一个结点, p是一个临时变量用来缓存t的值

void reverse(node_t *head)

{

node_t *p = 0, *q = 0, *t = 0;

for (t = head; t; p = t, t = t->next, p->next = q, q = p);

}

测试:

node_t d = {"d", 0}, c = {"c", &d}, b = {"b", &c}, a = {"a", &b};

list_display(&a);

reverse(&a);

list_display(&d);

3.查找倒数第k个元素(尾结点记为倒数第0个)

算法:2个指针p, q初始化指向头结点.p先跑到k结点处, 然后q再开始跑, 当p跑到最后跑到尾巴时, q正好到达倒数第k个.复杂度O(n)

node_t *_kth(node_t *head, int k)

{

int i = 0;

node_t *p = head, *q = head;

for (; p && i < k; p = p->next, i++);

if (i < k) return 0;

for (; p->next; p = p->next, q = q->next);

return q;

}

测试:

node_t d = {"d", 0}, c = {"c", &d}, b = {"b", &c}, a = {"a", &b};

printf("_0 :%s _1: %s _2:%s _3:%s\n", _kth(&a, 0)->data, _kth(&a, 1)->data, _kth(&a, 2)->data, _kth(&a, 3)->data);

输出:

_0 :d _1: c _2:b _3:a

4.查找中间结点

找出中间的那个结点

算法:设两个初始化指向头结点的指针p, q.p每次前进两个结点, q每次前进一个结点, 这样当p到达链表尾巴的时候, q到达了中间.复杂度O(n)

node_t *middle(node_t *head)

{

node_t *p, *q;

for (p = q = head; p->next; p = p->next, q = q->next){

p = p->next;

if (!(p->next)) break;

}

return q;

}

测试:

node_t e = {"e", 0}, d = {"d", &e}, c = {"c", &d}, b = {"b", &c}, a = {"a", &b};

printf("%s\n", middle(&a)->data);

5.逆序打印链表

给你链表的头结点, 逆序打印这个链表.使用递归(即让系统使用栈), 时间复杂度O(n)

void r_display(node_t *t)

{

if (t){

r_display(t->next);

printf("%s", t->data);

}

}

6.判断一个链表是否有环

如果一个链表有环, 那么它肯定只有一个环.(一个相交结点)

算法:设两个指针p, q, 初始化指向头.p以步长2的速度向前跑, q的步长是1.这样, 如果链表不存在环, p和q肯定不会相遇.如果存在环, p和q一定会相遇.(就像两个速度不同的汽车在一个环上跑绝对会相遇).复杂度O(n)

int any_ring(node_t *head)

{

node_t *p, *q;

for (p = q = head;p; p = p->next, q = q->next){

p = p->next;

if (!p) break;

if (p == q) return 1; //yes

}

return 0; //fail find

}

测试:

node_t e = {"e", 0}, d = {"d", &e}, c = {"c", &d}, b = {"b", &c}, a = {"a", &b};

e.next = &a;

printf("%d\n", any_ring(&a));

7.找出链表中环的入口结点

先看下面这张图:

0818b9ca8b590ca3270a3433284dd417.png

我们设链表的无环的部分长度为L1,即有L1个节点,注意,这个L1是包括环的入口节点的。然后让环的长度是L2,这个L2也是包括环的入口节点。这个时候,p1和p2的交点如图所示,交点距离环的入口节点为a(从入口节点沿着行走方向走到交点),即在环的入口节点后面的第a个节点,就是交点,我用红色标记出a。

然后我们来考察一下L1,L2,a,以及n(n是走过的步数,不是走过的节点数,p1一步一个节点,p2一步两个节点)的关系。

忘记说一点了,我们可以明确的是,p1在进入环后,走了不到一圈就在交点处和p2重合,為什麼肯定没有走完一圈?因为p1在进入环的时候,p2和p1之间的距离(沿着行走方向)至多为 L2-1,不可能超过L2-1,因为环的大小也才只有L2 。p2追赶p1,最多只需要走L2-1步,因为每走一步,p1和p2的相对距离减小1,那么p1最多只走了L2-1步,就是最多只经过了L2-1个节点,不可能走完一圈。

现在可以列公式了:

L1+a=n                   #1   //n是p1走过的节点数

L1+k*L2+a=2*n     #2   //2*n这个是p2走过的节点数,其中的k表示p2可能在环里面走了k圈,k>=1

由#2式减去#1式,有:

k*L2 = n                   #3

同时由#1和#3得到:

L1+a = k*L2            #4

接着由#4就得到了如下式:

L1 = k*L2 - a = (k-1)*L2 + (L-a)

得到这条式子就拨得云开见月明啊有木有,因为(L-a)表示的是交点与环入口的距离(从交点沿着行走方向到环入口),然后(k-1)是>=0的,因为p2在环中至少绕了一圈,这样我们就发现:L1的长度 = 环长度的整数倍 + 交点与环入口的距离

也就是说,p1再走L1步就可以达到环的入口。问题是L1不是已知的,没关系,在表头设置一个p3指针,p3每步前进一个节点。让p1和p3同时走,每次走1步,等p3和p1重合了,就是到了环口的位置了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是单链表反转的C语言实现代码: ```c #include<stdio.h> #include<stdlib.h> struct ListNode { int val; struct ListNode *next; }; struct ListNode* reverseList(struct ListNode* head) { struct ListNode* prev = NULL; struct ListNode* curr = head; while (curr != NULL) { struct ListNode* nextTemp = curr->next; curr->next = prev; prev = curr; curr = nextTemp; } return prev; } int main() { struct ListNode* head = (struct ListNode*)malloc(sizeof(struct ListNode)); head->val = 1; head->next = (struct ListNode*)malloc(sizeof(struct ListNode)); head->next->val = 2; head->next->next = (struct ListNode*)malloc(sizeof(struct ListNode)); head->next->next->val = 3; head->next->next->next = NULL; struct ListNode* new_head = reverseList(head); while (new_head != NULL) { printf("%d ", new_head->val); new_head = new_head->next; } return 0; } ``` 这段代码中,我们首先定义了一个 `ListNode` 结构体,表示单链表的节点。其中,`val` 表示节点的值,`next` 表示指向下一个节点的指针。接着,我们定义了 `reverseList` 函数,用于反转单链表。在函数中,我们使用了三个指针变量 `prev`、`curr` 和 `nextTemp`,分别表示当前节点的前一个节点、当前节点和当前节点的后一个节点。在循环中,我们不断地将当前节点的 `next` 指针指向前一个节点,然后更新三个指针变量的值,直到当前节点为 `NULL`。最后,我们返回反转后的单链表的头指针 `prev`。在 `main` 函数中,我们构造一个简单的单链表,并且调用 `reverseList` 函数进行反转,最后输出反转后的单链表的节点值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值