题目:
给定一个链表的头结点,检查链表是否是回文,返回结果true or false
分析:
方法1;刚学过的求链表的中点,求出中点从中间向两边依次对比
方法2:利用栈,先将链表全部压入栈,之后从栈弹出的时候就相当于与将链表反转,正序遍历链表,与栈弹出的元素对比,每一个袁术都相同,返回true(为什么不从两端遍历,单向链表?)
代码:
package main
import (
"errors"
"fmt"
)
//链表本身,包含长度,头结点和尾节点,对于一个双向链表,
//我们只关注它的haea和tail,head和tail是一个个具体的节点
type LinkedList struct {
len int
head *LinkedNode
tail *LinkedNode
}
//链表中的一个节点的结构,包含节点的数据,前向指针,后向指针,指向的也是一个节点本身
type LinkedNode struct {
data int
next *LinkedNode
prev *LinkedNode
}
// type Data struct{
// key string
// data int
// }
//所有的操作都基于指针
func NewLinkedList() *LinkedList {
return &LinkedList{
len: 0,
head: nil,
tail: nil,
}
}
//追加一个节点,链表没有头结点就把自己设为链表的头结点,头尾指针都指向自己
//有头结点就把自己放在最后,1先把自己的prev指向原来的tail,2把原来的tail的next指向自己,3把链表的tail设为自己
func (ll *LinkedList)Add(node *LinkedNode) {
// node := &LinkedNode{
// data: data,
// }
// fmt.Printf("hhhhhhead%v\n", ll.head)
if ll.head==nil{
ll.tail=node
ll.head=node
}else{
// fmt.Printf("tttttail%v\n", ll.tail)
node.prev=ll.tail
ll.tail.next=node
ll.tail=node
}
ll.len++//记得更新长度
}
//奇数返回中点 偶数返回上中点
func Midorupmid(head LinkedNode) *LinkedNode{
if &head ==nil || head.next == nil || head.next.next == nil{//只有一个节点或两个节点时,返回头结点
return &head
}
//慢指针一个定位在第二个节点,快指针定位在第三个节点
slow:=head.next
fast:=head.next.next
for fast.next != nil && fast.next.next !=nil{
slow=slow.next
fast=fast.next.next
}
return slow
}
//1 for里面的条件是&,不是||
//2 slow和 fast在前进过程中是不等距的,不能混淆
func Midordownmid(head LinkedNode) *LinkedNode{
if &head ==nil || head.next == nil {//控制节点大于1,就可以进入下面的循环
return &head//当head等于nil,head的next也等于nil,
}
//两个指针都初始化在head.next
slow:=head.next
fast:=head.next
//节点数大于1进入
for fast.next !=nil && fast.next.next !=nil {//注意是and,必现同时满足
slow=slow.next
fast=fast.next.next//slow和fast不等距,所以不能用slow.next.next代替
// fmt.Println(slow,fast)
}
return slow
}
//
//利用链表找中点的方法
func ismirror(head LinkedNode) bool{
//不知道链表长度是奇是偶,所以调用求中点和上下中点去分别找到两个指针,
//1 既可以利用返回的两个指针再向两边移动去比值
//2 在不知道奇偶的条件下也可以实现
t1:=Midorupmid(head)
t2:=Midordownmid(head)
fmt.Println(t1,t2)
if (*t1).data==(*t2).data{//值相等等
fmt.Printf("true\n")
for (*t1).data==(*t2).data && t1.prev !=nil && t2.next !=nil{
t1=t1.prev
t2=t2.next
fmt.Printf("ininin %v %v \n",t1,t2)
}
if t1.prev ==nil && t2.next ==nil && (*t1).data==(*t2).data {
return true
}
}
return false
}
type Stack []int
// 入栈
func (s *Stack) push(a int) {
*s = append(*s, a)
}
// 出栈
func (s *Stack) pop() (int, error) {
if (len(*s) == 0) {
return 0, errors.New("Empty Stack")
}
a := *s
defer func() {
*s = a[:len(a) - 1]
}()
return a[len(a) - 1], nil
}
//借助栈实现,先将链表全部压入栈,再输出的时候相当于将链表反转
//栈弹出元素(倒序)与正序遍历链表的值对比,全相等就返回true
func ismirror1(head *LinkedNode)bool {
node:=head
var s Stack = make([]int,0)
s.push(head.data)
for head.next !=nil{
s.push(head.next.data)
head=head.next
}
fmt.Println(s)
for node !=nil{
t1,_:=s.pop()
t2:=node.data
fmt.Println(t1,t2)
if t1 != t2{
return false
}
node=node.next
// fmt.Println(node)
}
return true
}
func main(){
var head LinkedNode
head.data=18
var l1 LinkedNode
l1.data=5
var l2 LinkedNode
l2.data=20
var l3 LinkedNode
l3.data=3
var l4 LinkedNode
l4.data=3
var l5 LinkedNode
l5.data=20
var l6 LinkedNode
l6.data=5
var l7 LinkedNode
l7.data=18
list := NewLinkedList()
list.Add(&head)
list.Add(&l1)
list.Add(&l2)
list.Add(&l3)
list.Add(&l4)
list.Add(&l5)
list.Add(&l6)
list.Add(&l7)
// fmt.Println(list.head)
// fmt.Println(list.head.next)
// fmt.Println(list.head.next.next)
// fmt.Println(list.head.next.next.next)
// fmt.Println(list.tail)
// fmt.Println(Midorupmid(head))
// fmt.Println(Midordownmid(head))
// fmt.Println(beforeMidorupmid(head))
// fmt.Println(aftereMidordownmid(head))
fmt.Println(ismirror(head))
fmt.Println(ismirror1(&head))
}
总结:
1 注意越界问题,f如判断ast.next.next是否nil,需保证第一个next不是nil
2 注意指针与值,结构体是指针,其中一个字段不是指针,取这个字段的值要用(*node).data