iOS 单链表算法的一些问题

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。如下图:

下面处理的全部是单链表

单链表的基本结构:

1 typedef struct node {
2     char *data;
3     struct node *next;
4 } node_t;

设定一个打印链表的函数:

1 void list_display(node_t *head)
2 {
3     for (; head; head = head->next)
4         printf("%s ", head->data);
5     printf("\n");
6 }

1.计算一个链表的长度(复杂度O(n))

算法:定义一个p指针指向头结点,步长为1,遍历链表。

1 int list_len(node_t *head)
2 {
3     int i;
4     for (i = 0; head; head = head->next, i++);
5     return i;
6 }

2.反转链表(复杂度O(n))

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

1 void reverse(node_t *head)
2 {
3     node_t *p = 0, *q = 0, *t = 0;
4     for (t = head; t; p = t, t = t->next, p->next = q, q = p);
5 }

3.查找倒数第k个元素(尾结点记为倒数第0个)(复杂度O(n))

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

1 node_t *_kth(node_t *head, int k)
2 {
3     int i = 0;
4     node_t *p = head, *q = head;
5     for (; p && i < k; p = p->next, i++);
6     if (i < k) return 0;
7     for (; p->next; p = p->next, q = q->next);
8     return q;
9 }

4.查找中间结点(复杂度O(n))

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

1 node_t *middle(node_t *head)
2 {
3     node_t *p, *q;
4     for (p = q = head; p->next; p = p->next, q = q->next){
5         p = p->next;
6         if (!(p->next)) break;
7     }
8     return q;
9 }

5.逆序打印链表(复杂度O(n))

算法:使用递归(即让系统使用栈)。

1 void r_display(node_t *t)
2 {
3     if (t){
4         r_display(t->next);
5         printf("%s", t->data);
6     }
7 }

6.判断一个链表是否有环(复杂度O(n))

算法:设两个指针p, q, 初始化指向头.p以步长2的速度向前跑, q的步长是1.这样, 如果链表不存在环, p和q肯定不会相遇.如果存在环, p和q一定会相遇。

 1 int any_ring(node_t *head)
 2 {
 3     node_t *p, *q;
 4     for (p = q = head;p; p = p->next, q = q->next){
 5         p = p->next;
 6         if (!p) break;
 7         if (p == q) return 1; //yes
 8     }
 9     return 0; //fail find
10 }

7.找出链表中环的入口结点(复杂度O(n))

算法:初始化三个指针p,q,r全部指向head,然后p以2的步长行进,q以1的步长行进。当p和q相遇的时候,发出r指针以1的步长行进,当p和r相遇的结点就是环的入口结点。

 1 node_t *find_entry(node_t *head)
 2 {
 3     node_t *p, *q, *r;
 4
 5     for (p = q = head; p; p = p->next, q = q->next){
 6         p = p->next;
 7         if (!p) break;
 8         if (p == q) break;
 9     }
10
11     if (!p) return 0; //no ring in list
12
13     for (r = head, q = q->next; q != r; r = r->next, q = q->next);
14
15     return r;
16 }

解析:

使用俩指针p和q, p扫描的步长为1, q扫描的步长为2.它们的相遇点为图中meet处(在环上).

假设头指针head到入口点entry之间的距离是K.则当q入环的时候, p已经领先了q为: d = K%n(n为环的周长).

我们设meet处相对entry的距离(沿行进方向)为x, 则有

(n-d)+x = 2x (p行进的路程是q的两倍)

解得x = n-d

那么当p和q在meet处相遇的时候, 从head处再发出一个步长为1的指针r, 可以知道, r和q会在entry处相遇!

8.判断两个链表是否相交(复杂度O(m+n))

算法:两个指针遍历这两个链表,如果他们的尾结点相同,则必定相交。

1 int is_intersect(node_t *a, node_t *b)
2 {
3     if (!a || !b) return -1; //a or b is NULL
4     for (; a->next; a = a->next);
5     for (; b->next; b = b->next);
6     return a == b?1:0; //return 1 for yes, 0 for no
7 }

9.找两个相交的链表的交点

算法:p,q分别遍历链表a,b,假设q先到达NULL,此时从a的头结点发出一个指针t,当p到达NULL时,从b的头结点发出s,当s==t的时候即相交。

 1 node_t *intersect_point(node_t *a, node_t *b)
 2 {
 3     node_t *p, *q, *k, *t, *s;
 4     for (p = a, q = b; p && q; p = p->next, q = q->next);
 5
 6     k = (p == 0)?q:p; //k record the pointer not NULL
 7     t = (p == 0)?b:a; //if p arrive at tail first, t = b ; else p = a
 8     s = (p == 0)?a:b;
 9     for (; k; k = k->next, t = t->next);
10     for (; t != s; t = t->next, s = s->next);
11     return t;
12 }

10.删除结点d(不给头结点)

算法:把下一个结点e的数据拷贝到d结点的数据区,然后删除e。(缺陷:不能删除尾结点)

1 node_t *delete(node_t *d)
2 {
3     node_t *e = d->next;
4     d->data = e->data;
5     d->next = e->next;
6 }

11.两个链表右对齐打印。

算法:p和q两个指针分别遍历链表a和b,假如q先到达NULL(即a比b长),此时由a头结点发出指针t,打印完整的链表a。同时p继续移动到NULL,并且打印空格。同时还从b头结点发出指针s打印完整的链表b。

 1 void foo(node_t *a, node_t *b)
 2 {
 3     node_t *p, *q, *k, *t, *s;
 4     for (p = a, q = b; p && q; p = p->next, q = q->next);
 5
 6     k = p?p:q;
 7     t = p?a:b;
 8     s = p?b:a;
 9
10     for (; t; printf("%d ", t->data), t = t->next);
11     printf("\n");
12     for (; k; printf("  "), k = k->next);
13     for (; s; printf("%d ", s->data), s = s->next);
14 }

转自:https://github.com/hit9/oldblog/blob/gh-pages/blog-src/blog/C/posts/25.mkd

时间: 2024-11-05 02:24:14

iOS 单链表算法的一些问题的相关文章

数据结构之自建算法库——单链表

本文针对数据结构基础系列网络课程(2):线性表中第10课时单链表基本操作的实现,建立单链表数据存储结构基本操作的算法库. 按照"0207将算法变程序"[视频]部分建议的方法,建设自己的专业基础设施算法库. 单链表算法库算法库采用程序的多文件组织形式,包括两个文件: 1.头文件:linklist.h,包含定义顺序表数据结构的代码.宏定义.要实现算法的函数的声明: #ifndef LINKLIST_H_INCLUDED #define LINKLIST_H_INCLUDED typedef

2-5-归并链式存储的单链表-线性表-第2章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第2章  线性表 - 归并单链表(链式存储) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SinglyLinkedList.c        相关测试数据下载

Leetcode23---&gt;Merge K sorted Lists(合并k个排序的单链表)

题目: 合并k个排序将k个已排序的链表合并为一个排好序的链表,并分析其时间复杂度 . 解题思路: 类似于归并排序的思想,lists中存放的是多个单链表,将lists的头和尾两个链表合并,放在头,头向后移动,尾向前移动,继续合并,直到头和尾相等,此时已经归并了一半, 然后以同样的方法又重新开始归并剩下的一半.时间复杂度是O(logn),合并两个链表的时间复杂度是O(n),则总的时间复杂度大概是O(nlogn):合并两个单链表算法可以参考Leetcode21中的解法:http://www.cnblo

单链表操作(数据结构实验一)

 实验内容 1 初始化一个带表头结点的单链表. 2 从表头不断插入结点建立一个带表头结点的单链表.设表中元素的类型为整型,元素值从键盘输入. 3 从表尾不断插入结点建立一个带表头结点的单链表.设表中元素的类型为整型,元素值从键盘输入. 4 打印一个带表头结点的单链表. 5 清空一个带表头结点的单链表. 代码:(只是把各个函数写好,并给出了调用数据,其他根据实验要求改就行了) #include<stdio.h> #include<stdlib.h> #include<mallo

4.2.2 算法之美--单链表实现

按照书上的要求实现了一下单链表:单链表的实现可能以前看过几次了:现在想想最主要的几个操作算法应该能够写了吧:遇到的问题: 1. 链表节点写成private;所已给出了访问的接口: 2.模板类的.h和.cpp实现写在同一个文件: 3.感觉以后的数据结构实现还是用纯c的实现好一些:然后书主要是思路 节点类: #ifndef SINGLELIST_LISTNODE_H_ #define SINGLELIST_LISTNODE_H_ template <class T> class ListNode

单链表倒置算法

LinkList reverse(LinkList L)//单链表的倒置算法 { LNode *p,*q; p=L->next; L->next=NULL; while(p) { q=p; p=p->next; q->next=L->next; L->next=q; } return L; }

009实现一个算法来删除单链表中的一个结点,只给出指向那个结点的指针(keep it up)

呵呵,这个题不能直接删除已知的结点,因为是单链表,不知道前驱,只知道 后继结点,直接删除会使链表断开.不过我们可以删除已知结点的后继结点, 把后继结点的值赋值给已知结点. #include <iostream> struct Node { int data; Node* next; }; bool removeNode(Node* vNode) { if (vNode == NULL || vNode->next == NULL) return false; Node* pNext =

实现一个算法从一个单链表中返回倒数第n个元素(keep it up)

我们维护两个指针, 它们之间的距离为n.然后,我将这两个指针同步地在这个单链表上移动,保持它们的距离 为n不变.那么, 当第二个指针指到空时,第一个指针即为所求. #include <iostream> struct Node { int data; Node* next; }; void initList(Node* vNode) { for (int i=0; i < 20; ++i) { Node* TempNode = new Node; TempNode->data =

单链表的创建算法

单链表的创建算法 当一个序列中只含有指向它的后继结点的链接时,就称该链表为单链表. 单链表的示意图如下: Head指针为单链表的头指针,单链表L:L既是单链表的名字,也是其头指针.链表中的最后一个结点的指针域定义为空指针(NULL). 单链表的定义: struct Node { ElemType data; struct Node *next; }; typedef struct Node LNode; typedef struct Node *LinkedList; 单链表有带头结点和不带头结