链表——题目集合

package linkedList

import (
	"fmt"
	"testing"
)

/*
链表问题
面试时链表解题的方法论
1.对于笔试,不用太在乎空间复杂度,一切为了时间复杂度
2.对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法(吸引面试官要你不要别人)

链表面试题常用的数据结构
1.使用容器 哈希表、数组等
2.快慢指针


快慢指针
1.输入链表头节点,奇数长度返回中点,偶数长度返回上中点
2.输入链表头节点,奇数长度返回中点,偶数长度返回下中点
3.输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个
4.输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个

四种题边界条件不一样
*/



type Node struct {
	Value int
	Next *Node
}

func midOrUpMidNode(head *Node) *Node {
	if head == nil || head.Next == nil || head.Next.Next == nil {
		return head
	}

	slow, fast := head.Next, head.Next.Next
	for fast.Next != nil && fast.Next.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
	}
	return slow
}

func midOrDownMidNode(head *Node) *Node {
	if head == nil || head.Next == nil  {
		return head
	}

	slow, fast := head.Next, head.Next
	for fast.Next != nil && fast.Next.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
	}
	return slow
}


func midOrUpMidPreNode(head *Node) *Node {
	if head == nil || head.Next == nil || head.Next.Next == nil  {
		return head
	}

	slow, fast := head, head.Next.Next
	for fast.Next != nil && fast.Next.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
	}
	return slow
}


func midOrDownMidPreNode(head *Node) *Node {
	if head == nil || head.Next == nil  {
		return nil
	}

	if head.Next.Next == nil {
		return head
	}

	slow, fast := head, head.Next
	for fast.Next != nil && fast.Next.Next != nil {
		slow = slow.Next
		fast = fast.Next.Next
	}
	return slow
}






/*
常见面试题
给定一个单链表的头节点head,请判断该链表是否为回文结构
1.栈的方法特别简单(笔试用)      栈 全放进去 弹出比较  or  数组 从后往前遍历
2.改原链表的方法就需要注意边界了(面试用)

少一点的空间,用快慢指针,奇数的时候,定位到中点  偶数的时候定位到上中点 ,然后把上中点后边的数加入栈中


彻底不用容器
奇数个的时候,对中点后所有元素逆序  然后从前往后比对,返回答案之前,调节好指针指向,保证和最初一样
偶数个的时候,让上中点指向nil,对下中点后的元素逆序
*/

func isOakubdrine(head *Node) bool {
	if head == nil || head.Next == nil {
		return true
	}
	n1, n2 := head, head

	for n2.Next != nil && n2.Next.Next != nil { // 找到中点
		n1 = n1.Next
		n2 = n2.Next.Next
	}

	n2 = n1.Next // n2 右侧第一个节点
	n1.Next = nil
    var	n3 *Node
    for n2 != nil {
		n3 = n2.Next
		n2.Next = n1
		n1 = n2
		n2 = n3
	}

	n3 = n1
	n2 = head
	res := true
	for n1 != nil && n2 != nil {
		if n1.Value != n2.Value {
			res = false
			break
		}
		n1 = n1.Next
		n2 = n2.Next
	}
	n1 = n3.Next
	n3.Next = nil
	for n1 != nil {
		n2 = n1.Next
		n1.Next = n3
		n3 = n1
		n1 = n2
	}
	return res
}

func NewList(val ... int) *Node {
	root := &Node{
		Value: 0,
		Next:  nil,
	}

	cur := root
	for k := range val {
		node := &Node{
			Value: val[k],
			Next:  nil,
		}
		cur.Next = node
		cur = cur.Next
	}
	root = root.Next
   return root
}

func TestList(t *testing.T)  {
	head := NewList(1,2,3,4,5,6,7,8,9)
	fmt.Println(isOakubdrine(head))
	fmt.Println(midOrUpMidNode(head).Value)
	fmt.Println(midOrDownMidNode(head).Value)
	fmt.Println(midOrUpMidPreNode(head).Value)
	fmt.Println(midOrDownMidPreNode(head).Value)
}

/*
常见面试题
将单向链表按某只划分成左边小、中间相等、右边大的形式
1.把链表放入数组中,在数组上做partition (笔试用)
2.分成小中大三部分,再把各个部分之间串起来 (面试用)

 */

func listPartition(head *Node, pivot int) *Node {
	var sH,sT, eH,eT,mH, mT *Node
	for head != nil {
		if head.Value < pivot {
			if sH == nil {
				sH = head
				sT = head
			}else {
				sT.Next = head
				sT = sT.Next
			}
		}else if head.Value == pivot {
			if eH == nil {
				eH = head
				eT = head
			}else {
				eT.Next = head
				eT = eT.Next
			}
		}else {
			if mH == nil {
				mH = head
				mT = head
			}else {
				mT.Next = head
				mT = mT.Next
			}
		}
		head = head.Next
	}

	//小于区域的尾巴,连接等于区域的头,等于区域的尾巴连大于区域的头

	if  sT != nil {
		sT.Next = nil
	}

	if eT != nil {
		eT.Next = nil
	}

	if mT != nil {
		mT.Next = nil
	}


	if sT != nil {
		sT.Next = eH
		if eT == nil {
			eT = sT
		}
	}

	if eT != nil {
		eT.Next = mH
	}

	if sH != nil {
		return sH
	}

	if eH != nil {
		return eH
	}

	return mH
}

func TestListPartition(t *testing.T)  {
	head := NewList(1,2,2,33,33,4,5,5,6,8,6,88,99,6,7,8,9,189,26,69,65,6,7,5,6)
	head = listPartition(head,189)
	for head != nil {
		fmt.Print(head.Value,"->")
		head = head.Next
	}
}

/*
常见面试题
一种特殊的单链表节点类型描述如下
  type Node struct {
      value  int
      Next   *Node
      Rand   *Node
  }
Rand指针是单链表节点结构中新增的指针,Rand可能指向链表中的任意一个节点,也可能指向nil
给定一个由Node节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。
要求:时间复杂度O(N), 额外空间复杂度O(1)


    __________________________
    |                        |
    |                        '
    0 --------->  1 -------> 2 ---------> 3 -----> nil
                  |                       '
                  |                       |
                  -------------------------

   next 指针无环

 先遍历 建立对应关系
   map[*Node]*Node  key 老类型  value 新类型
  再建立Rand的关系
*/

type node struct {
	value int
	next  *node
	rand  *node
}

func copyListWithRand1(head *node) *node {
	if head == nil {
		return nil
	}


	dic := map[*node]*node{}
	cur := head
	for cur != nil {
		dic[cur] = &node{
			value: cur.value,
			next:  nil,
		}
	}
	cur = head
	for cur != nil {
	   dic[cur].next = dic[cur.next]
	   dic[cur].rand = dic[cur.rand]
	   cur = cur.next
	}
	return dic[head]
}


/*

   __________________________
   |                        |
   |                        '
   0 --------->  1 -------> 2 ---------> 3 -----> nil
                 |                       '
                 |                       |
                 -------------------------



   ______________________________________________________
   |                                                    |
   |                                                    '
   0 -----> 0' ---->  1 ---> 1' ----> 2 -----> 2' ----> 3 ---> 3' --> nil
                      |               '
                      |               |
                      -----------------
 */


func copyListWithRand2(head *node) *node {
	if head == nil {
		return nil
	}
	var cur, next  *node
	cur = head
	for cur != nil {
		next = cur.next
		cur.next = &node{
			value: cur.value,
			next:  nil,
			rand:  nil,
		}

		cur.next.next = next
		cur = next
	}
	cur = head
	var curCopy *node
	for cur != nil {
		next = cur.next.next
		curCopy = cur.next
		if cur.rand != nil {
			curCopy.rand = cur.rand.next
		}else {
			curCopy.rand = nil
		}
		cur = next
	}

	res := head.next
	cur = head
	//分离
	for cur != nil {
		next = cur.next.next
		curCopy = cur.next
		cur.next = next
        if next != nil {
		  curCopy.next = next.next
	    }else {
		  curCopy.next = nil
	    }
	    cur = next
	}
	return  res
}



/*
该题和约瑟夫环问题称为链表问题的两个噩梦

常见面试题
给定两个可能有环也可能无环的单链表,头节点head1和head2
请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交,返回null
要求:
如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)

和值没关系,跟内存地址有关系
hashSet 就可以 存内存地址  先查有没有 没有放进去,  有 就是第一个相交节点

环内 6个点 环外8个点

快慢指针
先找到相遇节点 让F指向头,再次相遇就是第一个相交节点  小学奥数的追及问题
 */
//找到链表第一个入环节点,如果无环,返回nil

func getLoopNode(head *Node) *Node {
	if head == nil || head.Next == nil || head.Next.Next == nil {
		return nil
	}

	n1, n2 := head.Next, head.Next.Next
	for n1 != n2 {
		if n2.Next == nil || n2.Next.Next == nil {
			return nil
		}
		n2 = n2.Next.Next
		n1 = n1.Next
	}
	n2 = head
	for n1 != n2 {
		n1 = n1.Next
		n2 = n2.Next
	}
	return  n1
}


/*
        head1      head2
        loop1      loop2
       两个无环链表相交,必用最后的公共部分,用hash表方式找第一个相交节点,可以

       不用hash表
       head1 ....... 走到最后一个节点  假设长度 100
       head2 ....... 走到最后一个节点  假设长度 80
      判断两个节点地址是否相同  不相同则不相交
      长链表先走 100 -80   短链表和长链表一起走 一定相遇

    1. loop1   loop2  都为nil
    2. 一个为nil 另一个不为nil     // 不可能相交
    2. 一个为nil 另一个不为nil     // 不可能相交
    3. 都不为nil  3种情况   >都有环,不相交 、 >都有环,两个链表入环节点时一个、    >入环节点不是一个
    如果两个有环节点相交 两个链表一定公用环
 */


//如果量链表都无环,返回第一个相交节点,如果不相交,返回nil
func noLoop(head1, head2 *Node) *Node {
	if head1 == nil || head2 == nil {
		return nil
	}
	cur1, cur2 := head1, head2
	n := 0
	for cur1.Next != nil {
		n++
		cur1 = cur1.Next
	}
	for cur2.Next != nil {
		n--
		cur2 = cur2.Next
	}
	if cur1 != cur2 {
		return  nil
	}


	// n : 链表1长度 减去 链表2 长度的值
	cur1 = head2
	if n > 0 {
		cur1 = head1
	}

	cur2 = head1
	if cur1 == head1 {
		cur2 = head2
	}
	n = abs(n)
	for n != 0 {
		n--
		cur1 = cur1.Next
	}
	for cur1 != cur2 {
		cur1 = cur1.Next
		cur2 = cur2.Next
	}
	return cur1
}

func abs(n int) int {
	if n < 0 {
		return  -n
	}
	return  n
}



//两个有环链表,返回第一个相交节点,如果不相交,返回nil
func bothLoop(head1, loop1, head2,loop2 *Node) *Node {
    var cur1, cur2 *Node
	if loop1 == loop2 {
	    cur1 = head1
		cur2 = head2
		n := 0
		for cur1 !=  loop1 {
			n++
			cur1 = cur1.Next
		}

		for cur2 != loop2 {
			n--
			cur2 = cur2.Next
		}

		cur1 = head2
		if n > 0 {
			cur1 = head1
		}

		cur2 = head1
		if cur1 == head1 {
			cur2 = head2
		}
		n = abs(n)
		for n != 0 {
			n--
			cur1 = cur1.Next
		}

		for cur1 != cur2 {
			cur1 = cur1.Next
			cur2 = cur2.Next
		}
		return cur1
	}else {
		cur1 = loop1.Next
		for cur1 != loop1 {
			if cur1 == loop2 {
				return loop1
			}
			cur1 = cur1.Next
		}
		return nil
	}
}



/*
这是一个综合题目
1.给一个链表,返回第一个入环节点
2.两个无环链表相交,返回第一个相交节点
3.两个有环链表相交,找到第一个相交节点
 */
func getIntersectNode(head1,head2 *Node) *Node  {
	if head1 == nil || head2 == nil {
		return nil
	}
	loop1 := getLoopNode(head1)
	loop2 := getLoopNode(head2)
	if loop1 == nil && loop2 == nil {
		return noLoop(head1,head2)
	}
	if loop1 != nil && loop2 != nil {
		return bothLoop(head1,loop1,head2,loop2)
	}
	return nil
}



/*
常见面试题
能不能不给单链表的头节点,只给想要删除的节点,就能做到在链表上把这个点删除

有! 将下一个节点的值复制给自己 自己的next指针指向下一个节点的next位置  --- 》 借尸还魂
1.有问题,删掉的是下一个节点,只是替代了内容
2.如果非常复杂,可能无法copy
3.绝对无法删除链表最后一个节点


  node1 := Node{}
  node2 := Node{}
  node3 := Node{}
  node1.Next = node2
  node2.Next = node3

  node3 = nil 并未真正删除
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值