请判断一个链表是否为回文链表
示例812565218 将后半部分逆序得到81256 再与前半部分对比看是否相等
package main
import "fmt"
//***如字符串是通过单链表来存储的,那该如何来判断是一个回文串呢?(如level,noon)***//
// 那么单链表怎么存储字符串?
type StrNode struct {
str string
next *StrNode //这个表示指向下一个结点
}
// 给链表插入一个结点,在单链表的最后加入
func InsertStrNode(head *StrNode, newStrNode *StrNode) {
//1.先找到最后一个结点
//2.创建一个辅助结点
temp := head
for {
//3.如果temp的next指向空,那就是最后一个结点
if temp.next == nil {
break
}
temp = temp.next //4.让temp不断指向下一个结点
}
//5.将newStrNode加入到链表的最后
temp.next = newStrNode
//fmt.Println("这就连上了")
}
// 显示链表的所有结点信息
func ListStrNode(head *StrNode) {
//1.创建一个辅助结点
temp := head
//2.判度该链表是不是空
if temp == nil || temp.next == nil {
fmt.Println("空链表,无法显示")
return
}
//3.遍历这个链表
for {
fmt.Printf("[%s]=>", temp.str)
//4.判断结点是否到链表结尾
if temp.next == nil {
fmt.Println("")
break
}
temp = temp.next //5.指向下一个结点
}
}
// 找到链表中间节点,将后半部分转置,再从左向右遍历对比
func IsPalindrome(head *StrNode) bool {
if head == nil || head.next == nil {
return true
}
res := true
n1 := head
n2 := head
//快慢指针 快指针走两步 慢指针走一步,当快指针走完,慢指针刚好走到中点(偶数个走到中点左边的位置)
for n1.next != nil && n2.next != nil && n2.next.next != nil {
n1 = n1.next
n2 = n2.next.next
}
//将后半部分逆序
n2 = n1.next
n1.next = nil
//先将5>6给n1 2>1>8给回n2
//然后2>5>5给n1 1>8 给回n2
//直到给完n2
for n2 != nil {
n3 := n2.next
n2.next = n1
n1 = n2
n2 = n3
}
n3 := n1
n2 = head
//将逆序部分与前半部分比对
for n1 != nil && n2 != nil {
if n1.str != n2.str {
res = false
break
}
n1 = n1.next
n2 = n2.next
}
//将后半部分还原
//n3 =[8]=>[1]=>[2]=>[5]=>[6]=>
n1 = n3.next
//n1= [1]=>[2]=>[5]=>[6]=>
n3.next = nil
for n1 != nil {
n2 = n1.next
n1.next = n3
n3 = n1
n1 = n2
}
return res
}
func main() {
//a := 812565218
//1.先创建一个头结点
head := &StrNode{
str: "8",
//这里没法定义next,next要指向谁,要通过下一个结点的主动加入链表来确定,这里先空着
}
//2.创建新的StrNode
str1 := &StrNode{
str: "1",
}
str2 := &StrNode{
str: "2",
}
str3 := &StrNode{
str: "5",
}
str4 := &StrNode{
str: "6",
}
str5 := &StrNode{
str: "5",
}
str6 := &StrNode{
str: "2",
}
str7 := &StrNode{
str: "1",
}
str8 := &StrNode{
str: "8",
}
//3.加入
InsertStrNode(head, str1)
InsertStrNode(head, str2)
InsertStrNode(head, str3)
InsertStrNode(head, str4)
InsertStrNode(head, str5)
InsertStrNode(head, str6)
InsertStrNode(head, str7)
InsertStrNode(head, str8)
//4.显示
ListStrNode(head)
//5.判断
res := IsPalindrome(head)
fmt.Println(res)
}
将单向链表按某值划分成左边小、中间相等、右边大的形式
简单做法:将链表每个节点遍历成list数组,再用快排讲过的partition分成左中右三块,再把数组串起来成链表
空间复杂度小的做法:
如4 6 3 5 8 5 2 ,按5 划分
设置六个变量:左边区域的头和尾,中间区域的头和尾,右边区域的头和尾,再将左边中间右边的头尾相连
package main
import "fmt"
type Node struct {
value int
next *Node //这个表示指向下一个结点
}
// 给链表插入一个结点,在单链表的最后加入
func InsertStrNode(head *Node, newNode *Node) {
//1.先找到最后一个结点
//2.创建一个辅助结点
temp := head
for {
//3.如果temp的next指向空,那就是最后一个结点
if temp.next == nil {
break
}
temp = temp.next //4.让temp不断指向下一个结点
}
//5.将newNode加入到链表的最后
temp.next = newNode
//fmt.Println("这就连上了")
}
// 显示链表的所有结点信息
func ListNode(head *Node) {
//1.创建一个辅助结点
temp := head
//2.判度该链表是不是空
if temp == nil {
fmt.Println("空链表,无法显示")
return
}
//3.遍历这个链表
for {
fmt.Printf("[%d]=>", temp.value)
//4.判断结点是否到链表结尾
if temp.next == nil {
fmt.Println("")
break
}
temp = temp.next //5.指向下一个结点
}
}
func listPartition2(head *Node, pivot int) *Node {
var sh, st, eh, et, mh, mt, next *Node
for head != nil {
next = head.next
head.next = nil
if head.value < pivot {
if sh == nil {
sh = head
st = head
} else {
st.next = head //注意这里的sh就变成了head[0]->head[1]
st = head
}
} else if head.value == pivot {
if eh == nil {
eh = head
et = head
} else {
et.next = head
et = head
}
} else {
if mh == nil {
mh = head
mt = head
} else {
mt.next = head
mt = head
}
}
head = next
}
if st != nil { //如果有小于区域
st.next = eh //将小于尾和等于头连起来
if et == nil { //如果没有等于区域
et = st
}
}
if et != nil {
et.next = mh //等于尾或者小于尾 连上大于头
}
//有小于就返回小于头
//没有小于返回等于头
//没有等于返回大于头
if sh != nil {
return sh
} else {
if eh != nil {
return eh
} else {
return mh
}
}
}
func main() {
//a := 812565218
//1.先创建一个头结点
head := &Node{
value: 8,
//这里没法定义next,next要指向谁,要通过下一个结点的主动加入链表来确定,这里先空着
}
//2.创建新的Node
n1 := &Node{
value: 1,
}
n2 := &Node{
value: 2,
}
n3 := &Node{
value: 5,
}
n4 := &Node{
value: 6,
}
n5 := &Node{
value: 5,
}
n6 := &Node{
value: 2,
}
n7 := &Node{
value: 1,
}
n8 := &Node{
value: 8,
}
//3.加入
InsertStrNode(head, n1)
InsertStrNode(head, n2)
InsertStrNode(head, n3)
InsertStrNode(head, n4)
InsertStrNode(head, n5)
InsertStrNode(head, n6)
InsertStrNode(head, n7)
InsertStrNode(head, n8)
//4.显示
ListNode(head)
//5.判断
res := listPartition2(head, 5)
ListNode(res)
}
复制含有随机指针的链表
一种特殊的单链表节点如下
type Node struct {
value int
next *Node //这个表示指向下一个结点
rand *Node //指向随机节点
}
给定一个由Node节点类型组成的无环单链表的头结点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。 【要求】时间复杂度O(n),额外空间复杂度O(1)
解1:利用map[node,node],遍历链表得到,key(old1) value (new1),key(old2) value(new2) ...map<old,new>
old1 的next是old2 ,那么new2的next是什么呢,是不是通过map[old2]找到,所以newN=map[oldN]
newN.next --> map[oldN].next=map[oldN.next]
newN.rand --> map[oldN].rand=map[oldN.rand]
解2:将1>2>3,转成1>1'>2>2'>3>3',如果1的rand是3,那么1'的rand就是3',等效于1的rand的next 代码实现
package main
import (
"fmt"
)
type Node struct {
value int
next *Node //这个表示指向下一个结点
rand *Node
}
// 显示链表的所有结点信息
func ListNode(head *Node) {
//1.创建一个辅助结点
temp := head
//2.判度该链表是不是空
if temp == nil {
fmt.Println("空链表,无法显示")
return
}
//3.遍历这个链表
for {
fmt.Printf("[%d]=>", temp.value)
//4.判断结点是否到链表结尾
if temp.next == nil {
fmt.Println("")
break
}
temp = temp.next //5.指向下一个结点
}
}
func copyListWithRand1(head *Node) *Node {
mapNode := make(map[*Node]*Node, 0)
cur := head
for cur != nil {
//这里只给了value,新copy的next和rand都为空
mapNode[cur] = &Node{value: cur.value}
cur = cur.next
}
cur = head
for cur != nil {
mapNode[cur].next = mapNode[cur.next]
mapNode[cur].rand = mapNode[cur.rand]
cur = cur.next
}
return mapNode[head]
}
func copyListWithRand2(head *Node) *Node {
cur := head
var next *Node
//1>2>3
for cur != nil {
next = cur.next
cur.next = &Node{value: cur.value}
cur.next.next = next
cur = next
}
//1>1'>2>2'>3>3'
ListNode(head)
cur = head
var copy *Node
for cur != nil {
next = cur.next.next
copy = cur.next
if cur.rand != nil {
copy.rand = cur.rand.next //比如1的rand是3 那么1'的rand就是3' 等效于3的next
}
cur = next
}
//split
cur = head
res := head.next //1>1'>2>2'>3>3' 拿到节点1'
for cur != nil {
next = cur.next.next //2>2'>3>3'
copy = cur.next //copy 1'>2>2'>3>3'
cur.next = next //1>2>2'>3>3'
if next != nil {
copy.next = next.next //1'>2'>3>3'
}
cur = next //2>2'>3>3'
}
return res
}
func main() {
//a := 812565218
//1.先创建一个头结点
head := &Node{
value: 0,
//这里没法定义next,next要指向谁,要通过下一个结点的主动加入链表来确定,这里先空着
}
//2.创建新的Node
n1 := &Node{
value: 1,
}
n2 := &Node{
value: 2,
}
n3 := &Node{
value: 3,
}
//3.加入
head.next = n1
n1.next = n2
n2.next = n3
head.rand = n3
n1.rand = head
n3.rand = n2
//res := copyListWithRand1(head)
res := copyListWithRand2(head)
ListNode(res)
}
请找到单链表第一个入环节点
graph TB
A((a))
B((b))
C((c))
D((d))
E((e))
A-->B
B-->C
C-->D
D-->E
E-->C
图中c就是第一个入环节点
解1:从a开始next遍历,判断a存在与否,不存在放进map。判断b存在与否,不存在放进map。判断c存在与否,不存在放进map,判断d存在与否,不存在放进map。判断e存在与否,不存在放进map,判断c存在与否,存在返回c
解2:不用map减少空间复杂度的做法,使用快慢指针。快指针一次走两步,慢指针一次走一步,如果快指针走到nil,说明没有环(有环就一定走不到结尾)。如果快指针和慢指针相遇,让快指针返回开头,快慢指针调整成每次走一步。快指针和慢指针相遇的地方就是第一个入环节点。(该方法比较魔性,建议记住就行)
代码实现
package main
import (
"fmt"
)
type Node struct {
value int
next *Node //这个表示指向下一个结点
rand *Node
}
// 显示链表的所有结点信息
func ListNode(head *Node) {
//1.创建一个辅助结点
temp := head
//2.判度该链表是不是空
if temp == nil {
fmt.Println("空链表,无法显示")
return
}
//3.遍历这个链表
for {
fmt.Printf("[%d]=>", temp.value)
//4.判断结点是否到链表结尾
if temp.next == nil {
fmt.Println("")
break
}
temp = temp.next //5.指向下一个结点
}
}
func firstLoop(head *Node) *Node {
if head == nil || head.next == nil || head.next.next == nil {
return nil
}
slow := head.next
fast := head.next.next
//快指针一次两步 慢指针一次一步
for slow != fast {
if fast.next == nil || fast.next.next == nil {
return nil
}
slow = slow.next
fast = fast.next.next
}
//快指针返回开头 ,快慢指针都一次一步
fast = head
for slow != fast {
slow = slow.next
fast = fast.next
}
return slow
}
func main() {
//a := 812565218
//1.先创建一个头结点
head := &Node{
value: 0,
//这里没法定义next,next要指向谁,要通过下一个结点的主动加入链表来确定,这里先空着
}
//2.创建新的Node
n1 := &Node{
value: 1,
}
n2 := &Node{
value: 2,
}
n3 := &Node{
value: 3,
}
n4 := &Node{
value: 4,
}
n5 := &Node{
value: 5,
}
//3.加入
head.next = n1
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n5
n5.next = n3
fmt.Println(firstLoop(head))
}
请找到两个单链第一个相交的节点
思路:按照是否有无环区分
- 如果都无环,它们只能是这种形态
graph TB A((a)) B((b)) C((c)) D((d)) E((e)) A1((a1)) A-->B B-->C C-->D D-->E A1-->C
我们遍历a 和 a1 ,获得长度l 和 l1,和结尾节点e 和e1,如果e和e1不相等,则不存在相交
如果相等,a先走步数l1-l,然后a和a1一起遍历,第一个相等的节点就是第一个相交的节点
-
如果一个有环,一个无环,则不存在相交(你可以画画图,是不存在这种相交情况的)
-
如果都有环又分为三种情况
- 情况一:
graph TB
A((a))
B((b))
C((c))
D((d))
E((e))
A1((a1))
B1((b1))
C1((c1))
D1((d1))
E1((e1))
A-->B
B-->C
C-->D
D-->E
E-->C
A1-->B1
B1-->C1
C1-->D1
D1-->E1
E1-->C1
这种第一个回环节点c不等于c1,将它走一遍loop,如果都找不到等于c1的节点,就返回空,不相交
- 情况二
graph TB
A((a))
B((b))
C((c))
D((d))
E((e))
F((f))
A1((a1))
A-->B
B-->C
C-->D
D-->E
E-->F
F-->D
A1-->C
这种第一个回环节点d=d1,将d当结尾节点,等效于情况一
- 情况三
graph TB
A((a))
B((b))
C((c))
D((d))
E((e))
F((f))
A1((a1))
B2((b2))
C2((c2))
A1-->B2
B2-->C2
C2-->F
A-->B
B-->C
C-->D
D-->E
E-->F
F-->C
这里的c和f都可以当做第一个相交节点
将a1的第一个回环节点f,再走一圈,如果找到另一个链表的回文节点c,则f或c就是第一个相交节点
代码实现
package main
import (
"fmt"
)
type Node struct {
value int
next *Node //这个表示指向下一个结点
rand *Node
}
// 显示链表的所有结点信息
func ListNode(head *Node) {
//1.创建一个辅助结点
temp := head
//2.判度该链表是不是空
if temp == nil {
fmt.Println("空链表,无法显示")
return
}
//3.遍历这个链表
for {
fmt.Printf("[%d]=>", temp.value)
//4.判断结点是否到链表结尾
if temp.next == nil {
fmt.Println("")
break
}
temp = temp.next //5.指向下一个结点
}
}
func firstLoop(head *Node) *Node {
if head == nil || head.next == nil || head.next.next == nil {
return nil
}
slow := head.next
fast := head.next.next
//快指针一次两步 慢指针一次一步
for slow != fast {
if fast.next == nil || fast.next.next == nil {
return nil
}
slow = slow.next
fast = fast.next.next
}
//快指针返回开头 ,快慢指针都一次一步
fast = head
for slow != fast {
slow = slow.next
fast = fast.next
}
return slow
}
func noLoop(head *Node, head1 *Node) *Node {
cur, cur1, diff := head, head1, 0
for cur.next != nil {
diff++
cur = cur.next
}
diff++ //算上尾节点
for cur1.next != nil {
diff--
cur1 = cur1.next
}
diff-- //算上尾节点
//两个无环单链的结尾值不一样,就是不相交
if cur != cur1 {
return nil
}
//长的给cur
if diff > 0 {
cur = head
cur1 = head1
} else {
cur = head1
cur1 = head
diff = -diff
}
//长的走diff步
for diff != 0 {
cur = cur.next
diff--
}
//找到第一个相交的节点
for cur1 != cur {
cur = cur.next
cur1 = cur1.next
}
return cur
}
func bothloop(head1, loop1, head2, loop2 *Node) *Node {
var cur1 *Node
var cur2 *Node
//情况二 将loop1当做结尾节点求两个无环相交
if loop1 == loop2 {
cur1 = head1
cur2 = head2
diff := 0
for cur1.next != loop1 {
diff++
cur1 = cur1.next
}
diff++ //算上尾节点
for cur2.next != loop2 {
diff--
cur2 = cur2.next
}
diff-- //算上尾节点
//长的给cur1
if diff > 0 {
cur1 = head1
cur2 = head2
} else {
cur1 = head2
cur2 = head1
diff = -diff
}
//长的先走diff步
for diff != 0 {
diff--
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
}
}
func getIntersectNode(head1, head2 *Node) *Node {
if head1 == nil || head2 == nil {
return nil
}
loop1 := firstLoop(head1)
loop2 := firstLoop(head2)
if loop1 == nil && loop2 == nil {
return noLoop(head1, head2)
}
if loop1 != nil && loop2 != nil {
return bothloop(head1, loop1, head2, loop2)
}
//一个有环一个无环的直接返回不存在
return nil
}
func main() {
//a := 812565218
//1.先创建一个头结点
head1 := &Node{
value: 0,
//这里没法定义next,next要指向谁,要通过下一个结点的主动加入链表来确定,这里先空着
}
//2.创建新的Node
n1 := &Node{
value: 1,
}
n2 := &Node{
value: 2,
}
n3 := &Node{
value: 3,
}
n4 := &Node{
value: 4,
}
n5 := &Node{
value: 5,
}
n6 := &Node{
value: 6,
}
n7 := &Node{
value: 7,
}
n8 := &Node{
value: 8,
}
n9 := &Node{
value: 9,
}
head2 := &Node{
value: 0,
//这里没法定义next,next要指向谁,要通过下一个结点的主动加入链表来确定,这里先空着
}
//2.创建新的Node
k1 := &Node{
value: 1,
}
k2 := &Node{
value: 2,
}
k3 := &Node{
value: 3,
}
k4 := &Node{
value: 4,
}
//k3 := &Node{
// value: 3,
//}
//k4 := &Node{
// value: 4,
//}
//k5 := &Node{
// value: 5,
//}
//3.加入
head1.next = n1
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n5
n5.next = n6
n6.next = n7
n7.next = n8
n8.next = n9
n9.next = n7
head2.next = k1
k1.next = k2
k2.next = k3
k3.next = k4
k4.next = n3
fmt.Println(getIntersectNode(head1, head2))
}