笔试:数据结构之链表

链表应该是面试时被提及最频繁的数据结构,链表的结构很简单,它由指针把若干个节点连接成链状结构。
链表的创建、插入节点、删除节点等操作都只需要20行左右的代码就能实现,因此适合面试时提问。
常见的面试题有:
1)从尾到头打印链表;
2)删除链表的节点;
3)删除链表中倒数第k个结点;
4)反转链表;
5)合并两个排序的链表;
6)两个链表的第一个公共结点;
7)判断链表中是否有环;找出环的入口
8)判断一个链表是否为回文;

对应的解题思路为:

1

1)首先从头到尾打印这个链表,将打印的值放入一个栈中,然后从栈中依次弹出元素。依赖栈的后入先出的结构实现了从尾到头打印链表。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
'''
需要注意的是,python没有自带的链表结构,也没有自带的栈结构,所以要实现已上思路的话,需要先了解题目中listNode的定义,其具有两个属性,next表示接下来的节点,val表示其值。栈结构需要自己实现。
'''
class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        class Stack(object):
            def __init__(self):
                self.item=[]
            def push(self,value):
                self.item.append(value)
            def pop(self):
                if self.item:
                    return self.item.pop()
                else:
                    return
            def isEmpty(self):
                return len(self.item)==0
        
        if not listNode:
            return []
        stack=Stack()
        while listNode:
            stack.push(listNode.val)
            listNode=listNode.next
        re=[]
        while not stack.isEmpty():
            re.append(stack.pop())
        return re

2

2)输入:将被删除的节点Node
将这个节点的数值赋值为下一个节点的数值,然后让它指向下下个节点.

Node.val=Node.next.val
Node.next=Node.next.next

也就是将下一个节点赋值给这个节点来实现删除这个节点的操作。

要考虑的特殊情况是:要删除的点是尾结点,那么只能从头结点开始遍历,找到这个结点的前一个结点;如果这个链表只有一个结点,那么就是删除投头结点,返回头结点为空。

3

  1. 在链表中,因为内存不是连续的,表示节点的位置需要一个辅助的指针来实现。
    比如说要找倒数第k个结点,那么就使用两个指针,fast在slow指针的前k个位置点,当fast指向none时,slow就指向倒数第k个结点。

    class Solution(object):
    def removeNthFromEnd(self, head, n):
    slow=fast=dummy=ListNode(0)
    dummy.next=head
    for i in range(n+1):
    fast=fast.next
    while fast:
    slow=slow.next
    fast=fast.next
    slow.next=slow.next.next
    return dummy.next #考虑删除的是head的情况

4

4)使用迭代的方法,依次保存下一个节点–>让这个节点指向上一个节点–>继续遍历下一个保存的节点。

pre=None
while head:
        Next=head.next
        head.next=pre
        pre=head
        head=Next
    return pre

5

5)采用递归的方法,每次比较链表最开始的两个节点,并将较小的一个值接在后面,然后依次比较后面的节点。

if not l1:
	return l2
if not l2:
	return l1
if l1.val>l2.val:
	a,b=l2,l1
else:
	a,b=l1,l2
a.next=self.sort(a.next,b)
return a

思考:如果是对两个已排序的数组合并呢?要求空间复杂度为O(1)

在合并两个数组时,如果从前往后幅值每个数字(字符)则需要重复移动数字(字符)多次,那么我们可以考虑从后往前幅值,这样就能减少移动的次数,从而提高效率。

6

6)当两个单链表有公共结点之后,它们后面的所有结点都是一样的。因此如果我们可以从最后面的数据开始比较,找到最后一个相同的节点就是第一个公共结点,可以使用栈来存储两个链表的节点来实现,可是这样需要O(M+N)的空间复杂度。不能从头开始比较的一个重要原因是:两个链表的长度不一样。因此可以先遍历两个链表得到他们的长度,就能知道哪个链表更长,以及长的链表比短的链表长多少;在第二次遍历的时候,先让长的链表多走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是他们的第一个公共结点。

class Solution(object):
    def getIntersectionNode(self, headA, headB):
	    if not headA or not headB:
		    return None
		a,b=headA,headB
	    while a is not b:
		    a=a.next if a else headB  #注意这里是if a而不是a.next
		    b=b.next if b else headA
		return a

为了考虑两者之间没有公共结点的情况,所以a=b=None的情况在第二次遍历时也存在。

7

7)从一个新颖的角度出发,如果链表中有环,那么两个人以不同的速度在环中跑,总有一天会遇到,如果直到跑得快指向空了还没遇到说明没有环。
如何找到环的入口呢?
首先我们已经找到位于环中的某一个节点,也就是当slow=fast时指向的节点,然后根据这个节点我们可以得到环的长度n,方法是从这个节点出发,一边继续向前移动一边计数,当再次回到这个节点的时候,就可以得到环的长度n;然后定义两个节点p1,p2为头结点,从头结点开始,p1节点先走n步,然后两个节点同时向前走,相遇的点就是环的入口。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        slow,fast=pHead,pHead
        while fast and fast.next:
            fast=fast.next.next 
            slow=slow.next
            if fast==slow: #1.判断是否有环:如果快慢指针相遇,说明有环
                aNode=slow.next
                n=1  #2.计算环的长度
                while slow!=aNode:  #aNode回到slow的步数n就是环的长度
                    n=n+1
                    aNode=aNode.next
                slow,fast=pHead,pHead #3.计算入环点:让快指针fast先走n步,慢指针从表头出发,相遇点即入环点
                while n:  
                    n=n-1
                    fast=fast.next
                while slow!=fast:
                    slow=slow.next
                    fast=fast.next
                return slow

当不需要计算环的长度n时,一个指针从相遇点即slow=fast时指向的节点出发,另一个指针从表头出发,然后两个节点同时向前走,相遇的点就是环的入口。

class Solution:
    def EntryNodeOfLoop(self, pHead):
        # write code here
        slow,fast=pHead,pHead
        while fast and fast.next:
            fast=fast.next.next 
            slow=slow.next
            if fast==slow: #如果快慢指针相遇,说明有环
                aNode=pHead
                while slow!=aNode:  #一个指针从相遇点出发,另一个指针从表头出发,会在入环点相遇
                    slow=slow.next
                    aNode=aNode.next
                return slow

8

8)回文的性质是从前往后读与从后往前读相同,也就是链表数值是对称的,那么可以将链表的前半部分逆转,然后比较这个逆转后的部分与后半部分的数值是否一致。

class Solution(object):
    def isPalindrome(self, head): 
        pre=None
        slow=fast=head
        while fast and fast.next:
            fast=fast.next.next
            Next=head.next
            head.next=pre
            pre=head
            head=Next
        if fast:
            head=head.next
        while pre and pre.val == head.val:
            pre=pre.next
            head=head.next
        if not pre and not head:
            return True
        else:
            return False

注意

1.代码的鲁棒性
要考虑链表的头结点为空,链表只有一个节点或链表长度小于k的情况,防止代码奔溃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值