赞
踩
对于列表的表示我们有如下两种实现形式:
// 定义双端链表的数据类型
type ListNode struct {
prev *ListNode
next *ListNode
value string
}
type DoubleList struct {
head *ListNode // 头节点
tail *ListNode // 尾节点
len int // 长度
lock sync.Mutex // 为了并发安全,引入锁
}
/* 一些常见的基本操作 */ // GetValue 获取节点值 func (node *ListNode) GetValue() string { return node.value } // GetPre 获取前一个节点 func (node *ListNode) GetPre() *ListNode { return node.prev } // GetNext 获取后一个节点 func (node *ListNode) GetNext() *ListNode { return node.next } // HasNext 是否有后一个节点 func (node *ListNode) HasNext() bool { return node.next != nil } // HasPre 是否有前一个节点 func (node *ListNode) HasPre() bool { return node.prev != nil } // IsNil 是否为空 func (node *ListNode) IsNil() bool { return node == nil } // Len 获取长度 func (list *DoubleList) Len() int { return list.len } // First 头部节点 func (list *DoubleList) First() *ListNode { return list.head } // Last 尾部节点 func (list *DoubleList) Last() *ListNode { return list.tail }
实现从头部开始,在某个节点之前插入一个节点。
// AddNodeFormHead 从头部开始,在某个节点之前插入一个节点 // 0 表示第一个元素之前,1 表示第二个元素之前... func (list *DoubleList) AddNodeFormHead(value string, pre int) { list.lock.Lock() defer list.lock.Unlock() if pre <= 0 || pre > list.len { panic("pre is out of range") } // 找到头部节点 node := list.head // 向后进行遍历,找到pre-1个节点 for i := 0; i <= pre; i++ { node = node.next } newNode := new(ListNode) node.value = value // 如果定位到的节点为空,则直接插入到头部 if node.IsNil() { list.head = newNode list.tail = newNode } else { // 找到前一个节点 pre := node.prev // 如果定位到的节点为头部,则直接插入到头部 if pre.IsNil() { newNode.next = node node.prev = newNode list.head = newNode } else { // 将新节点插入到定位节点之前 // 新节点的后一个节点为定位节点 pre.next = newNode newNode.prev = pre // 新节点的前一个节点为定位节点的前一个节点 node.next.prev = newNode newNode.next = node.next } } list.len++ }
实现从尾部开始,在某个节点之后插入一个节点。
// AddNodeFormTail 从尾部开始,在某个节点之后插入一个节点 // 0 表示第一个元素之后,1 表示第二个元素之后... func (list *DoubleList) AddNodeFormTail(value string, next int) { list.lock.Lock() defer list.lock.Unlock() // 找到尾部节点 node := list.tail if next <= 0 || next > list.len { panic("next is out of range") } // 向前进行遍历,找到next-1个节点 for i := 0; i <= next; i++ { node = node.prev } newNode := new(ListNode) newNode.value = value // 如果定位到的节点为空,则直接插入到尾部 if node.IsNil() { list.head = newNode list.tail = newNode } else { // 找到定位节点的后一个节点 next := node.next // 如果定位到的节点为尾部,则直接插入到尾部,需要更新尾部节点 if next.IsNil() { // 新节点的前一个节点为尾部节点 // 新节点的后一个节点为空 newNode.prev = node node.next = newNode list.tail = newNode } else { // 将新节点插入到定位节点之后 // 新节点的前一个节点为定位节点 newNode.prev = node node.next = newNode // 新节点的后一个节点为定位节点的后一个节点 newNode.next = next next.prev = newNode } } list.len++ }
实现从头部开始,获取第 n + 1
个位置上的节点。
// IndexFormHead 从头部开始获取第 n + 1 个位置上的节点,索引从零开始
func (list *DoubleList) IndexFormHead(n int) *ListNode {
if n > list.len || n < 0 {
panic("index is out of range")
}
// 找到头部节点
node := list.head
// 向后进行遍历,找到第 n 个节点
for i := 0; i < n; i++ {
node = node.next
}
return node
}
实现从尾部开始,获取第 n + 1
个位置上的节点。
// IndexFormTail 从尾部开始获取第 n + 1 个位置上的节点,索引从零开始
func (list *DoubleList) IndexFormTail(n int) *ListNode {
if n > list.len || n < 0 {
panic("index is out of range")
}
// 找到尾部节点
node := list.tail
// 向前进行遍历,找到第 n 个节点
for i := 0; i < n; i++ {
node = node.prev
}
return node
}
实现从头部开始,删除第 n + 1
个位置上的节点。
// RemoveNodeFormHead 从头部开始,删除第 n + 1 个位置上的节点,索引从零开始 func (list *DoubleList) RemoveNodeFormHead(n int) *ListNode { list.lock.Lock() defer list.lock.Unlock() if n >= list.len || n < 0 { return nil panic("index is out of range") } // 找到头部节点 node := list.head // 向后进行遍历,找到第 n 个节点 for i := 0; i < n; i++ { node = node.next } // 移除节点 pre := node.prev next := node.next // 如果前继和后继都为空,则直接删除头部节点 if pre.IsNil() && next.IsNil() { list.head = nil list.tail = nil } else if pre.IsNil() { // 表示移除的是头部节点,让下一个节点变成头部节点 list.head = next next.prev = nil } else if next.IsNil() { // 表示移除的是尾部节点,让前一个节点变成尾部节点 list.tail = pre pre.next = nil } else { // 前继和后继都不为空,则将后继节点的前继节点变成前继节点 pre.next = next next.prev = pre } list.len-- return node }
实现从尾部开始,删除第 n + 1
个位置上的节点。
// PopTailFromHead 从尾部开始往前找,获取第 n 个位置上的节点,并将移除返回 func (list *DoubleList) PopTailFromHead(n int) *ListNode { list.lock.Lock() defer list.lock.Unlock() if n >= list.len || n < 0 { return nil panic("index is out of range") } // 获取尾部元素 node := list.tail // 向前进行遍历,找到第 n 个节点 for i := 0; i < n; i++ { node = node.prev } // 移除的节点的前驱和后继 pre := node.prev next := node.next // 如果前驱和后继都为空,则直接删除尾部节点 if pre.IsNil() && next.IsNil() { list.head = nil list.tail = nil } else if pre.IsNil() { // 直接将后继节点变成尾部节点 list.head = next next.prev = nil } else if next.IsNil() { list.tail = pre pre.next = nil } else if next.IsNil() { pre.next = next pre.next = nil } else { pre.next = next next.prev = pre } list.len-- return node }
字典是存储键值对的数据结构,把一个键和一个值映射起来,一一映射。并且键不能重复。
func DicExample() { m := make(map[string]int64, 4) m["dog"] = 1 m["hen"] = 2 m["cat"] = 3 fmt.Println(m) which := "hen" v, ok := m[which] if ok { // find fmt.Println("finn", which, "value:", v) } else { // not find fmt.Println("not find", which) } }
在 Golang 中,实现集合是一件很有意思的事情。
我们通常借助空结构体为值来实现 Set, 因为我们知道字典的键是不重复的,所以只要我们不考虑字典的值,就可以来实现集合了。
注意:空结构体并不占用内存在大小。
// 思想:不考虑字典的值,我们可以实现一个set
type Set struct {
m map[int]struct{} // 为什么我们要使用空结构体,因为空结构体不占用内存
len int
sync.RWMutex
}
// NewSet 新建一个set
func NewSet(cap int64) *Set {
temp := make(map[int]struct{}, cap)
return &Set{
m: temp,
}
}
// Add 增加一个元素
func (s *Set) Add(item int) {
s.Lock()
defer s.Unlock()
s.m[item] = struct{}{}
s.len = len(s.m)
}
//Remove 移除一个元素
func (s *Set) Remove(item int) {
s.Lock()
defer s.Unlock()
if s.len == 0 {
return
}
// 从字典中删除
delete(s.m, item)
// 计算长度
s.len = len(s.m)
}
// Has 判断一个元素是否在set中
func (s *Set) Has(item int) bool {
s.RLock()
defer s.RUnlock()
_, ok := s.m[item]
return ok
}
// Len 获取set的长度
func (s *Set) Len() int {
return s.len
}
//IsEmpty 判断set是否为空
func (s *Set) IsEmpty() bool {
if s.len == 0 {
return true
}
return false
}
// Clear 清空set
func (s *Set) Clear() {
s.Lock()
defer s.Unlock()
s.m = make(map[int]struct{})
s.len = 0
}
// List 将 Set 转化为 Slice
func (s *Set) List() []int {
s.RLock()
defer s.RUnlock()
list := make([]int, 0, s.len)
for item := range s.m {
list = append(list, item)
}
return list
}
本次我们介绍使用Go语言实现数据结构中的列表和字典,并且介绍了一些常见的操作。数据结构这一系列我们没有涉及到具体的细节的讲解,适合有一定数据结构基础的童鞋,本系列代码已经上传至Github,欢迎大家 Star。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。