算法65-链表分区问题

题目

给定一个链表的头节点,和一个int,int的值是链表中一个节点的值,按小于int在左边,等于int在中间,大于int在右边将链表重新链接

分析:
链表的荷兰国旗问题,
方法1 :将链表的节点一次加入数组,以荷兰国旗的解法将链表排序,输出时一次链接链表元素

方法2:类似分区的思想,只是将区间分别用小于头结点(shead),小于尾节点(stail),等于头结点(ehead),等于尾节点(etail),大于头结点(mhead),大于尾节点(mtail)六个指针来划分三个分区,将符合条件的节点一次链接到对应到分区,最后将三个分区首尾相连

在这里插入图片描述

代码:

package main

import (
	"fmt"
	// "math"
	// "sort"
	// "time"
)

//链表本身,包含长度,头结点和尾节点,对于一个双向链表,
//我们只关注它的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 partition(list []*LinkedNode,num int) []int{
	more:=len(list)
	less:=-1
	index:=0
	for  index < more{
		// fmt.Println("here")
		if list[index].data<num{
			fmt.Printf("exchange idex  %v   less %v  ",index+1,less+1)
			swap(list,index,less+1)
			less++
			index++
			//小于去必然小于等于index,将一个小于num的的数放进小于区,不会引起小于区的混乱,
			//小于去已经是检查过的区域,必然符合规则,不用重新检测index位置
		}else if list[index].data>num{
			fmt.Printf("exchange idx  %v  more  %v  ",index,more-1)
			swap(list,index,more-1)
			more--//大于区的数未知,放到index位置的话可能引起混乱,所以index不推进,从当前index继续检测
		}else{//等于什么都不做,,所以不会引起任何混乱,直接推进index检查下一位
			index++
		}
		fmt.Println(index)

		fmt.Println(list)

	}
	fmt.Println(less+1,more-1)
	result:=[]int{less+1,more-1}
	return result
}

func swap(list []*LinkedNode,m,n int){
	tmp:=list[m]
	list[m]=list[n]
	list[n]=tmp
}
//实现1 
//将链表放进list进行partion分区,小于的在左边等于中间大于右边(荷兰国旗分区)
//将分好区的节点一次取出,取出的时候挂上next节点
func partitionlist(head *LinkedNode,num int){
	tmp:=[]*LinkedNode{}
	i:=0
	node:=head
	for node !=nil{
		i++
		tmp=append(tmp,node)
		node=node.next
	}
	partition(tmp,num)
	fmt.Printf("%#v\n",tmp)
	//先将0节点和1节点连接好,下面的遍历直接从1节点开始,从1开始是为了方便指定前后指针,如果是单向链表就不用
	tmp[0].next=tmp[1]
	tmp[0].prev=nil
	tmp[1].prev=tmp[0]
	for t:=1;t<i;t++{
		if t==i-1{
			tmp[t].next=nil
			
		}else{
			tmp[t].next=tmp[t+1]
			tmp[t+1].prev=tmp[t]
		}
		// fmt.Println(tmp[t])
		// fmt.Println(tmp[t].next)
		// fmt.Println("___________________________")

	}
	for t:=0;t<i;t++{
		fmt.Println(tmp[t].prev)
		fmt.Println(tmp[t])
		fmt.Println(tmp[t].next)

		fmt.Println("___________________________")
	}

}
//实现2
//借助分区思想,但分区是借助六个指针,小于区头指针shead,小于区尾指针stail,等于区头指针ehead,大于区头指针mhead以此类推
//遍历链表,对比值大于就放进大于分区,小于放进小于分区,等于放进等于分区(实际是靠指针实现,分区头尾指针标记对应的分区的地址,降低空间复杂度)
//最后将小于区尾指针指向等于区头指针,等于区尾指针指向大于区头指针,将三个分区按小-中-大首尾相连,(这里我简化成单向链表了)
func partitionlist1(head *LinkedNode,num int) *LinkedNode{
	var shead *LinkedNode
	var stail *LinkedNode

	var ehead *LinkedNode
	var etail *LinkedNode

	var mhead *LinkedNode
	var mtail *LinkedNode
	
	node:=head
	for node !=nil{
		// fmt.Println(node)
		// node.next=nil
		node.prev=nil
		if (*node).data<num{
			if shead !=nil{
				stail.next=node
				stail=node
			}else{
				shead=node
				shead.prev=nil
				stail=node
				// shead.next=stail
				//这里为什么不再需要挂后节点,因为初始head就等于tail,而且指定了tail的next。也就相当于指定了head的next
				//这里为什么不再需要挂后节点,因为初始head就等于tail,而且指定了tail的next。也就相当于指定了head的next

			}

		}else if (*node).data>num{
			if mhead !=nil{
				mtail.next=node
				mtail=node
			}else{
				mhead=node
				mhead.prev=nil
				mtail=node
				// mhead.next=mtail
			}

		}else{
			if ehead !=nil{
				etail.next=node
				etail=node
			}else{
				ehead=node
				ehead.prev=nil
				etail=node
				// ehead.next=etail
			}

		}
	node=node.next
	}
	//判断每个区是否为空,然后过滤掉为空的区后链起来
	if stail != nil {
		if ehead !=nil{//小于区不为空,等于区不为空,大于区不用判断了,直接链起来
			stail.next=ehead
			etail.next=mhead
			mtail.next=nil//初始的节点都有前后指针,这里要将做后一个节点的后指针置空
		}else{//小于区不为空,等于区为空,大于区不用判断,直接将小于区与大于区链起来
			if mtail != nil{//大于区不为空,等于区为空,将小于区与大于区链起来,将大于区tail的next置空
				stail.next=mhead
				mtail.next=nil
			}else{
				stail.next=mhead
			}
		}
	}else{//小于区为空
		if ehead != nil{
			if mtail !=nil{
				etail.next=mhead
				mtail.next=nil
				return ehead
			}

		}else{//小于区为空,等于区为空,直接返回大于区,不管是不是空
			return mhead
		}
	}

	return shead
}

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=4

  
    var l4 LinkedNode
    l4.data=3

	var l5 LinkedNode
    l5.data=8

	var l6 LinkedNode
    l6.data=12

	var l7 LinkedNode
	l7.data=12

	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)

	num:=17
	node:=partitionlist1(&head,num)
	fmt.Printf("return %#v\n" ,node)
	for node !=nil{
		fmt.Println(node)
		// fmt.Println(node.next)	
		fmt.Println("+-----------------------+")

		node=node.next
	}
}

}

需要注意的点:

1 链表相关的操作,与指针相关的,最好都用指针,尤其是方法2中实际就是依靠指针,
2 方法2 在第一次将一个节点加到分区的时候,头结点和尾节点都指向它,头尾指针仅仅是指针而已,指向的是同一个值,第二次接入节点的时候,将节点加到tail.next,因此产生了链接关系,不需要再次链接
3 初始链表中每个节点都有前后两个指针coding到时候要考虑将分好区的链表的UI后一个节点的为指针置空,否则会出现循环链表(前节点不考虑)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值