六、链表

1. 使用python定义链表结构

from util.Empty import Empty
from util.Outbound import Outbound

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

class LinkedList():
    def __init__(self):
        self.head = Node()
        self.tail = None
        self.length = 0

    def peek(self):
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        return self.head.next

    def get_first(self): # 得到第一个元素 
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        return self.head.next
        
    def get_last(self): # 得到最后一个元素
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        node = self.head
        while node.next != None:
            node = node.next
        return node
    
    def get(self, index): # 按索引得到元素 
        if (index < 0 or index >= self.length):
            raise Outbound( 'index is out of bound' );
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        node = self.head.next
        for i in range(index):
            node = node.next
        return node
                
    def add_first(self, value):
        node = Node(value, None)
        node.next = self.head.next
        self.head.next = node
        self.length += 1   
        
    def add_last(self, value):
        new_node = Node(value)
        node = self.head
        while node.next != None:
            node = node.next
        node.next = new_node
        self.length += 1

    def add(self, index, value):
        if (index < 0 or index > self.length):
            raise Outbound( 'index is out of bound' )
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        new_node = Node(value)
        node = self.head
        for i in range(index):
            node = node.next
        new_node.next = node.next;
        node.next = new_node;
        self.length += 1     
        
    def remove_first(self):
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        value = self.head.next
        self.head.next = self.head.next.next
        self.length -= 1
        return value    
        
    def remove_last(self):
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        node = self.head.next
        prev = self.head
        while node.next != None:
            prev = node
            node = node.next
        prev.next = None
        return node.value

    def remove(self, index):
        if (index < 0 or index >= self.length):
            raise Outbound( 'index is out of bound' );
        if not self.head.next:
            raise Empty( 'LinkedList is empty' )
        node = self.head
        for i in range(index):
            node = node.next
        result = node.next;
        node.next = node.next.next;
        self.length += 1     
        return result;      
        
    def printlist(self):
        node = self.head.next
        count = 0
        while node and count<20:
            print(node.value, end = " ")
            node = node.next
            count = count + 1
        print('')

2. 删除链表中的节点 

  • leetcode: 第237题. 删除链表中的节点,只允许访问要删除的那个节点。 难度:简单
  • leetcode: 面试题18. 删除链表的节点 与此题要求不一样,有可能包括尾节点也要删除
# 传统的方法是找到prev节点,然后让prev.next = prev.next.next 
# 现在不让访问prev了,只允许访问当前的node
def delete_node(node):  
    print(node.value)  
    node.value = node.next.value    # 偷梁换柱,把下一个节点的value赋值给当前节点的值,删除下一个节点即可。
    node.next = node.next.next

3. 找到链表的中间节点

  • leetcode 第876题. 链表的中间结点 难度:简单
# 快慢指针思想
def find_middle(lst):
    assert lst.head is not None and lst.head.next is not None
    
    head = lst.head
    fast = head
    slow = head
    
    while fast is not None and fast.next is not None:
        fast = fast.next.next
        slow = slow.next
    
    return slow.value

4. 确定链表是不是环形的

  • leetcode 第141题. 环形链表 难度:简单
    def has_cycle(lst):
        return has_cycle_helper(lst.head)
    
    def has_cycle_helper(head):
        if head is None:
            return False
        
        slow = head 
        fast = head 
        
        while fast is not None and fast.next is not None:
            fast = fast.next.next
            slow = slow.next
            
            if slow == fast:  # 环形的赛道一定会相遇
                return True
            
        return False

5.  给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

  • leetcode 第142题. 环形链表 II 难度:中等
  • 面试题 02.08. 环路检测 难度:中等
def find_beginning(head): 
    fast = head 
    slow = head 
    meet = None 
    while fast and fast.next is not None:
        fast = fast.next.next 
        slow = slow.next 

        if fast == slow:
            meet = fast 
            break 

    if meet is None or meet.next is None:
        return None 

    while head and meet:
        if head == meet:
            return meet 
        head = head.next
        meet = meet.next 

    return None

6.

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

  • leetcode 第19题. 删除链表的倒数第N个节点 难度:中等
  • 面试题22. 链表中倒数第k个节点 难度:简单
  • 面试题 02.02. 返回倒数第 k 个节点 
    def remove_nth(lst, n):
        assert n<=lst.length and n > 0
        
        fast = lst.head
        while n > 0:
            fast = fast.next
            n = n - 1
        
        if fast == None:
            return head 
            
        slow = lst.head
        while fast.next is not None:
            fast = fast.next
            slow = slow.next
            
        result = slow.next
        slow.next = slow.next.next
        
        lst.length = lst.length - 1
            
        return result

7. 把链表从中间切开成两个链表

def split(head):
    if (head is None):
        return
    slow = head
    fast = head
    front_last_node = slow
    while (fast is not None):  # 先找到中间节点
        front_last_node = slow
        slow = slow.next
        fast = fast.next.next if fast.next is not None else None
    front_last_node.next = None
    front = head
    back = slow
    return (front, back)

8. 合并两个有序的链表

  • leetcode 第21题. 合并两个有序链表 难度:简单
# iteratively
# O(m + n)
def mergeTwoLists1(l1, l2):
    dummy = cur = Node(0)
    while l1 and l2:
        if l1.value < l2.value:
            cur.next = l1
            l1 = l1.next
        else:
            cur.next = l2 
            l2 = l2.next
        cur = cur.next
    cur.next = l1 or l2
    return dummy.next

9.  找到两个链表的第一个公共节点

A: a1 → a2

                 ↘

                   c1 → c2 → c3

                 ↗    

B: b1 → b2 → b3

def getIntersectionNode(headA, headB):
    curA, curB = headA, headB
    lenA, lenB = 0, 0
    while curA is not None:
        lenA += 1
        curA = curA.next
    while curB is not None:
        lenB += 1
        curB = curB.next
    curA, curB = headA, headB
    if lenA > lenB:
        for i in range(lenA-lenB):
            curA = curA.next
    elif lenB > lenA:
        for i in range(lenB-lenA):
            curB = curB.next
    while curB != curA:
        curB = curB.next
        curA = curA.next
    return curA

10.  插入排序

  • leetcode 第147题. 对链表进行插入排序 难度:中等
# O(n^2)
def insertionSortList(head):
    dummy = Node(0)
    cur = head
    # pre is the sorted part
    # when see a new node, start from dummy
    # cur is the unsorted part
    while cur is not None:
        pre = dummy
        while pre.next is not None and pre.next.value < cur.value:
            pre = pre.next
        temp = cur.next # 插入
        cur.next = pre.next
        pre.next = cur
        cur = temp
    return dummy.next

11. 对链表进行排序

  • leetcode 第148题. 排序链表 难度:中等
# Merge sort的思想
def sortList(head):
    if head is None or head.next is None:
        return head
    mid = getMiddle(head)
    rHead = mid.next
    mid.next = None
    return merge(sortList(head), sortList(rHead))

def merge(lHead, rHead):
    dummyNode = dummyHead = Node(0)
    while lHead and rHead:
        if lHead.value < rHead.value:
            dummyHead.next = lHead
            lHead = lHead.next
        else:
            dummyHead.next = rHead
            rHead = rHead.next
        dummyHead = dummyHead.next
    if lHead:
        dummyHead.next = lHead
    elif rHead:
        dummyHead.next = rHead
    return dummyNode.next

def getMiddle(head):
    if head is None:
        return head
    slow = head
    fast = head
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next
    return slow 

 12. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

  • leetcode 第86题. 分隔链表 难度:中等
  • 面试题 02.04. 分割链表 难度:中等
def partition(head, x):
    left_head = Node(None)  # head of the list with nodes values < x
    right_head = Node(None)  # head of the list with nodes values >= x
    left = left_head  # attach here nodes with values < x
    right = right_head  # attach here nodes with values >= x
    # traverse the list and attach current node to left or right nodes
    while head:
        if head.value < x:  
            left.next = head
            left = left.next
        else:  # head.val >= x
            right.next = head
            right = right.next
        head = head.next
    right.next = None  # set tail of the right list to None
    left.next = right_head.next  # attach left list to the right
    return left_head.next  # head of a new partitioned list

 13.

  • 面试题24. 反转链表
  • leetcode 第206题. 反转链表 难度:简单
def reverse(lst):
    head = lst.head # 哨兵节点 
    result = None
    current = head.next 
    nxt = None
    
    while current is not None:
        nxt = current.next  # 事先存储元素
        current.next = result # 指向前一个元素 
        result = current # 赋值给新链表 
        current = nxt 
        
    head.next = result

14. 

  • leetcode 第92题. 反转链表 II 难度:中等
def reverseBetween(head, m, n):
    if m == n:
        return head

    dummyNode = Node(0)
    dummyNode.next = head
    pre = dummyNode

    for i in range(m - 1): # 先定位到m位置
        pre = pre.next

    # reverse the [m, n] nodes
    result = None
    current = pre.next # 再使用第一种方法反转
    for i in range(n - m + 1):
        nxt = current.next
        current.next = result
        result = current
        current = nxt

    pre.next.next = current
    pre.next = result

    return dummyNode.next

15. 

  • leetcode 第25题. K 个一组翻转链表 难度:困难
def reverseKGroup(head, k):
    if head is None or k < 2:
        return head
    
    next_head = head
    for i in range(k - 1):
        next_head = next_head.next
        if next_head is None:
            return head
    ret = next_head # 先用ret记录住当前第一个反转列表的位置,也是整个反转列表的开头,反转完返回它即可。
    
    current = head
    while next_head:
        tail = current  # 先用tail记录住当前current的位置
        prev = None
        for i in range(k):
            if next_head:
                next_head = next_head.next
            nxt = current.next
            current.next = prev
            prev = current
            current = nxt
        tail.next = next_head or current
    return ret

16. 

  • leetcode 第234题. 回文链表 难度:简单
  • 面试题 02.06. 回文链表 难度:简单
# 找到中间节点和中间节点的前一个节点
def isPalindrome(head):
    rev = None
    slow = fast = head  
    while fast and fast.next:
        fast = fast.next.next
        rev, rev.next, slow = slow, rev, slow.next
    if fast:
        slow = slow.next
    while rev and rev.value == slow.value:
        slow = slow.next
        rev = rev.next 
    return not rev 

17. 

Given 1->1->2, return 1->2.

Given 1->1->2->3->3, return 1->2->3.

  • leetcode 第83题. 删除排序链表中的重复元素 
def deleteDuplicates(head):
    if head == None:
        return head

    node = head

    while node.next:
        if node.value == node.next.value:
            node.next = node.next.next
        else:
            node = node.next

    return head

18. 

Given 1->2->3->3->4->4->5, return 1->2->5.

Given 1->1->1->2->3, return 2->3.

  • leetcode 第82题. 删除排序链表中的重复元素 II 难度:中等
def deleteDuplicates2(head):
    dummy = pre = Node(0)
    dummy.next = head
    while head and head.next:
        if head.value == head.next.value:
            while head and head.next and head.value == head.next.value:
                head = head.next
            head = head.next
            pre.next = head
        else:
            pre = pre.next
            head = head.next
    return dummy.next

  

 

posted @ 2021-02-24 19:15  RamboBai  阅读(132)  评论(0编辑  收藏  举报