玩转单链表

玩指针是许多算法的精髓,就是要像六脉神剑一样,指针乱指一通之后,内存中的数据变得井然有序。

操作指针真的是太好玩了。下面出几道题。

一、不用额外空间翻转单链表

给定一个单链表,要求翻转之。

import random

"""
O(1)空间复杂度翻转链表
方法:one->two->three 三元组构成一个窗口,每次都翻转窗口内的元素,然后将
窗口从左往右滑动
"""

list_length = 4

class Node:
    def __init__(self, value, nex):
        self.value = value
        self.next = nex

def generate_list():
    a = Node(0, None)
    now = a
    for i in range(list_length):
        x = Node(random.randint(0, 100), None)
        now.next = x
        now = x
    return a.next

def print_list(x):
    while x:
        print(x.value, end=' ')
        x = x.next
    print()

def reverse(x):
    one = x
    two = one.next
    one.next = None
    # 如果只有一个元素,那么直接返回
    if not two: return x
    three = two.next
    while 1:
        two.next = one
        if not three: break
        temp = three.next
        three.next = two
        one = two
        two = three
        three = temp
    return two

l = generate_list()
print_list(l)
print_list(reverse(l))

二、两个无环单链表求第一个公共结点

方法非常简单:求出两个链表长度之差,记为x;然后让较长的链表先走x步;最后让两个链表携手并进。

时间复杂度:O(N+M)
空间复杂度:O(1)

三、判断单链表中是否有环,如果有环,找出环的第一个结点

有一个著名的ro算法,甲乙两个人同时从起点出发,甲每次走1格,乙每次走两格。如果甲乙二人有一人走到了终点,那么说明无环;如果甲乙二人都没有走到终点,而是经过s次走路之后,甲乙二人相遇了,说明有环,并且环的长度必然是s。因为在x次走路中,甲比乙少走了s步,也就是说环的长度是s。

----x--|----y----|
       |____z____|

如上图所示,将链表三部分的长度记为x,y,z,甲乙二人在y线段的最右侧相遇。那么甲走了x+y步,乙走了x+y+z+y步,显然(x+y)乘以2等于x+y+z+y。所以z=x。要想找到环中的第一个结点,只需要发现一个规律:让乙回到起点处(x最左侧)且将乙的步伐改为每次走一格,甲继续从y的右侧(相遇点)出发,两人下次相遇的地方就是环中的第一个结点,因为x=z!

有一道类似的趣题:无限一维空间中有两个生物,这两个生物之间无法进行交流,现在让你用如下四条汇编指令编程:
(1)move left
(2)move right
(3)if this position is visited,then goto 语句标号
(4)goto语句(程序中可以使用标号)

现在你需要开发一套汇编代码,给这两个生物安装上这套程序,保证这两个生物一定会相遇。
代码如下:

loop:
    if this position is visited,then goto movefast
    moveleft
    goto loop
movefast:
    moveleft
    moveleft
goto loop

原理是:两个生物一前一后同时往一个方向走,速度相同。如果看见了另一个生物的轨迹,就开始提速。这样就肯定能够追上前面的生物。

四、给定两个单链表,判定它们是否拥有共同结点

---------          \________
          /
_________/

这个问题比较复杂,需要分类讨论。
1、如果两个链表都是无环单链表,那么只需要判断最后一个结点是否相同即可!
2、如果一个链表是无环单链表,而另一个链表是有环的,那么这两个链表必然无公共结点!
3、如果两个链表都是有环的:
(1)假如两个链表相交,那么环必然是这两个链表的公共部分,只需要找到链表1环上的某个结点x,看看链表2是否会经过结点x
(2)假如两个链表不相交,那么它们的环必然不相交

五、求两个有环单链表的第一个公共结点(两个链表保证有公共结点)

首先用ro算法求出环上的某个点,把这个点当做链表的结尾,相当于把这个结点的next设为null。从而问题转换成了两条无环单链表求第一个公共结点。这个问题复杂度也是O(M+N)

参考资料

http://blog.csdn.net/v_JULY_v/article/details/6447013

原文地址:https://www.cnblogs.com/weiyinfu/p/8563574.html

时间: 2024-11-01 16:38:04

玩转单链表的相关文章

韩顺平_PHP程序员玩转算法公开课(第一季)02_单链表在内存中存在形式剖析_学习笔记_源代码图解_PPT文档整理

文西马龙:http://blog.csdn.net/wenximalong/链表——最灵活的数据结构链表用来解决复杂的问题和算法是很方便的. 内容介绍1.什么是链表2.单向链表3.双向链表4.环形链表5.使用环形链表解决约瑟夫问题 链表——什么是链表链表是有序的列表,但是它在内存中是分散存储的. 链表无处不在,比如在操作系统中,文件和文件之间,文件块和文件块之间,是靠链表链接起来的.使用链表可以解决类似约瑟夫问题,排序,索引,二叉树,广义表... 链表——单链表的快速入门使用head头的单向链表

数据结构-单链表&单循环链表

第一次听到链表这个词的时候觉得很酷,可能玩游戏觉得链是武器,固定思维了,哈哈. 接口: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _001_线性表 { interface IListDS<T> { int GetLength(); void Clear(); bool IsEmpty()

单链表(linux c)

因为之前对链表反转这一块一直记忆不清,现在复习总结一下,只要还是要了解复习指针地址操作,结构体定义. 方法1:采用前插得方法,把节点从前边取出来然后再插到前边;分三步(具体看分解图) <一>先将第一个数字节点和头断开,然后接到链表最后 <二>然后按照上边的方法把剩下的链表中的节点一个接一个的插到前边 <三>直到将所有的节点插完,然后将新的头结点和head相连 先上完整的反转代码..链表中存入了15个数 void fanxu(list *plist) { //标记首先需要

单链表逆置

重写单链表逆置,熟能生巧- #include <iostream> #include <cstdlib> using namespace std; typedef struct List{ int num; struct List *next; }ListNode,*pListNode; void display(ListNode *pHead) { while(pHead) { cout<<pHead->num<<"--"; pH

02 单链表

线性表之链式存储---单链表 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 // 数据结构 6 typedef struct node 7 { 8 int data; 9 struct node *next; 10 }linkList; 11 12 // 创建单链表,并初始化 13 linkList *linkList_init(void) 14 { 15 linkList *l

(单链表)单链表的整体逆序和局部逆序

题目一:将单链表翻转. 思路:有三种方式. 一:用数组存储单链表的值,然后重新逆序赋值,效率较低. 二:利用三个指针,在原来的基础上进行逆序.这种方法比较实用,效率也高. 三:从第2个节点到第N个节点,依次逐节点插入到第1个节点(head节点)之后,最后将第一个节点挪到新表的表尾.需要新建一个链表,这种方法和第二种差不多. 这里我就写出第二种方法,比较实用. 代码(方法二): struct ListNode { int val; ListNode *next; ListNode(int x) :

[c语言]单链表的实现

一.基础知识:链表(线性表的链式存储结构) (1)特点:逻辑关系相邻,物理位置不一定相邻. (2)分类: a.不带头节点 b.带头节点 (3)单链表的存储结构: typedef struct SListNode {  DataType data;  struct SListNode* next; }SListNode; 二.代码实现(因避开使用二级指针,所以代码中使用了c++中的引用):此处构造的为不带头节点的链表 (1)sList.h   #pragma once typedef int Da

单链表基本操作

//头文件 #pragma once #include <stdio.h> #include <assert.h> #include <malloc.h> #include <stdlib.h> typedef int DateType; typedef struct LinkNode {  DateType _data;  struct  LinkNode* _next; } LinkNode; void PrintList(LinkNode* pHead

C++单链表的创建与操作

链表是一种动态数据结构,他的特点是用一组任意的存储单元(可以是连续的,也可以是不连续的)存放数据元素.链表中每一个元素成为“结点”,每一个结点都是由数据域和指针域组成的,每个结点中的指针域指向下一个结点.Head是“头指针”,表示链表的开始,用来指向第一个结点,而最后一个指针的指针域为NULL(空地址),表示链表的结束.可以看出链表结构必须利用指针才能实现,即一个结点中必须包含一个指针变量,用来存放下一个结点的地址.结点中只有一个next指针的链表称为单链表,这是最简单的链表结构. 首先定义一个