数据结构 の 链表和串
1.1双向链表的基本操作
package main
import "fmt"
type Node struct {
Data int
PrePoint *Node
NextPont *Node
}
type LinkList struct {
head *Node
current *Node
tail *Node
}
func main() {
datas := []int{1, 21, 31, 51, 62, 2, 3, 42, 33, 12, 12}
linklists := new(LinkList)
for _, v := range datas {
node := new(Node)
node.Data = v
InsertLinkList(node, linklists)
}
node := linklists.head
ShowLinkList(node)
}
func InsertLinkList(node *Node, link *LinkList) {
if link.head == nil {
link.head = node
link.current = node
link.tail = node
} else {
link.tail.NextPont = node
node.PrePoint = link.tail
link.tail = node // 只移动尾,头指针一直不动,中间的指针也一直不动
}
}
func ShowLinkList(node *Node) {
for node != nil {
fmt.Println(node.Data)
node = node.NextPont
}
}
1.2 将两个递增的链表合成一个
二.串简单的模式匹配算法
时间复杂度 O(mn)
package main
import "fmt"
func Index(a, a1 string) int {
i, j, k := 0, 0, 0
s := []byte(a)
p := []byte(a1)
for i < len(s) && j < len(p) {
if s[i] == p[j] {
i++
j++
} else {
j = 0 // 如果没有找到的话,i接着走,k+1
k++
i = k // 匹配不上的话,才会走下边这条路,所以k能记录一开始匹配的位置
}
}
if j >= len(p) {
return k
} else {
return 0
}
}
func main() {
index := Index("absegabciegeggegeabcacbab", "ciegegge")
fmt.Println(index)
}
KMP
查考文献https://www.zhihu.com/question/21923021
package main
import "fmt"
func KMP(t1, p1 string) int {
t := []byte(t1)
p := []byte(p1)
next := getNext(p1)
i, j := 0, 0
for i < len(t) && j < len(p) {
if j == -1 || t[i] == p[j] { //j=-1 是因为next的数组的第一个是-1
i++
j++
} else {
// 只要匹配了,i和 j 就一起往前走,如果不匹配,那么就让 j 回朔到 next[j] 的位置
j = next[j]
}
}
if j == len(p) {
return i - j
} else {
return -1
}
}
func getNext(p1 string) []int {
next := make([]int, 20)
next[0] = -1
p := []byte(p1)
// 看一下那个图,上边的模板字符串和下边的模板字符串是错开的,上边的1,对应下边的0,所以上边的0,应该对应下边的-1
// j=-1是为了好算,不然模板字符串自己和自己匹配,第一个是个特殊情况。
i, j := 0, -1
for i < len(p) {
if j == -1 || p[i] == p[j] {
i++
j++
next[i] = j
} else {
// 根据已经建好的next数组回朔
// 和上面kmp一样
j = next[j]
}
}
return next
}
func main() {
a := "ababaeabacaaaaaddfdfdfdfdf"
b := "aca"
rel := KMP(a, b)
fmt.Println(rel)
}
// 输出8
三.树
树的定义
由唯一的根和若干互不相交的子树,每一颗子树又是一棵树。
相关概念
- 结点的度:拥有子树的个数
- 树的度:树中各节点度的最大值
- 双亲节点:
- 祖先节点:他上边所有的节点都是祖先节点
- 森林:把根去掉,剩下的树就构成了森林
树的存储结构
顺序存储:一般使用称双亲存储,一组数组就可以搞定
如知道了节点 i,那么 tree[i] 就是 i 的双亲节点
链式存储包括:
- 孩子存储结构.
- 孩子兄弟存储结构。
二叉树
在普通树上再加两个条件,就构成了完全二叉树。
- 每个节点最多有两个子树
- 子树有左右之分,不能颠倒
二叉树又分为满二叉树,完全二叉树,完全二叉树是由满二叉树由右到左,从下到上排着删得到的。不能跳着删除
二叉树主要性质
- 非空二叉树的叶子结点数,等于双分支结点数+1;
- 在二叉树的第 i 层上,最多有 2i-1个结点。
对于完全二叉树的第 i 结点来说:
- i 的双亲节点为 【i/2】向下取整
- 如果
n>=2i
那么i
的左孩子的编号为2i
,如果n<2i
则无左结点 - 如果
n>=2i+1
,则右节点为2i+1
,如果n<2i+1
则无右节点
二叉树的遍历
- 先序遍历
type treeNode struct {
data int
lchild *treeNode
rchild *treeNode
}
// 先序遍历
func preorder(treenode *treeNode) {
if treenode != nil {
Visit(treenode)
preorder(treenode.lchild)
preorder(treenode.rchild)
}
}
- 总序遍历
- 后序遍历
- 层次遍历
二叉树的层次遍历
func Level(node *BTNode) {
que := make([]*BTNode, 20) //20长的循环队列
front, rear := 0, 0
if node != nil {
rear = (rear + 1) % 20
que[rear] = node // 根节点入队
for front != rear { // 如果不是空队
front = (front + 1) % 20
q := que[front] // 跟节点出队
Visit(q)
if q.lchilid != nil { // 如果有左节点就是入队
rear = (rear + 1) % 20
que[rear] = q.lchilid
}
if q.rchilid != nil { // 如果有右节点就入队
rear = (rear + 1) % 20
que[rear] = q
}
}
}
}
森林还有树
森林还有树之间的转换,孩子兄弟链表的存储方式,具体还是看书吧。
赫夫曼树 (最小代价树)
赫夫曼树又叫最优二叉树,它的特点是带权路径最短。
赫夫曼树的构造过程:
- 先从所有的节点中,找出两个权值最小的节点
- 将这两个节点构成一个新的树,然后,然后根节点权值就是左右之和
- 把这个节点放到之前的节点中去
- 以此类推着写
赫夫曼树的特点:
- 权值越大,和根节点的距离越近
- 树中没有度为 1 的节点,这类树叫做严格二叉树
- 树的带权路径长度最短
为了将图和树做区分。往往将图中的节点,叫做顶点。两个定点中间存在边,则为相邻关系。
- 注意有向图的边称为
弧
,分为弧头还有弧尾
。 - 有向图还分
如度
和出度
。
深度优先算法
package main
import "fmt"
type ArcNode struct { //邻接表
data int // 要指向的节点
nextNode *ArcNode // 下一个节点
}
type VNode struct {
data int //顶点
firstArcNode *ArcNode // 指向第一个节点
}
type AGraph struct {
vnodes []*VNode // 邻接表
vnumber int // 定点个数
arcNumber int //边的个数
}
var (
visit [20]int // 标记访问过的点
)
func NewGraph(v, arc int) AGraph { //创建一个新的表
agraph := new(AGraph)
agraph.vnumber = v // 节点的数目
agraph.arcNumber = arc // 边的数目
return *agraph
}
func DFS(aGraph *AGraph, i int) {
p := aGraph.vnodes[i].firstArcNode // 第一个定点
visit[i] = 1
fmt.Println("节点数", i) // 打印节点
p = p.nextNode // p指向下一个节点
for p != nil {
if visit[p.data] == 0 {
DFS(aGraph, p.data)
} else {
p = p.nextNode
}
}
}
func main() {
}
深度优先和广度优先算法
package main
import "fmt"
type ArcNode struct { // 根据图记忆
i int
next *ArcNode
}
type VNode struct {
i int
firstArc *ArcNode
}
type AGraph struct {
n, e int // 表点
adjList []VNode
}
func newAGraph(n int) *AGraph {
adjList := make([]VNode, n)
for i := 0; i < len(adjList); i++ {
adjList[i].i = i
}
return &AGraph{
n: n,
e: 0,
adjList: adjList,
}
}
func addArc(G *AGraph, from, to int) {
newarcNode := &ArcNode{
i: to,
}
if G.adjList[from].firstArc == nil {
G.adjList[from].firstArc = newarcNode
} else {
p := G.adjList[from].firstArc
for p.next != nil {
p = p.next
}
p.next = newarcNode
}
}
var visitedDFS [10]int
func DFS(G *AGraph, v int) { // 深度优先遍历
visitedDFS[v] = 1 //标记为访问过了
fmt.Println("深度优先遍历:", v)
p := G.adjList[v].firstArc
for p != nil {
if visitedDFS[p.i] == 0 {
DFS(G, p.i)
}
p = p.next
}
}
// 广度优先和树的层次遍历,都是不需要递归的
func BFS(G *AGraph) { // 广度优先遍历
var visited [20]int
var queue [20]int
front, rear := 0, 0
rear++
for front != rear {
front = (front + 1) % 20
j := queue[front] // 先出队
p := G.adjList[j].firstArc // 循环的关注点,是定点的子节点,而不是节点本身
for p != nil {
if visited[p.i] == 0 {
fmt.Println("中序遍历这个节点", p.i)
visited[p.i] = 1
rear = (rear + 1) % 20
queue[rear] = p.i
}
p = p.next
}
}
}
func main() {
G := newAGraph(5)
addArc(G, 0, 1)
addArc(G, 0, 3)
addArc(G, 0, 4)
addArc(G, 1, 2)
addArc(G, 1, 4)
addArc(G, 2, 0)
addArc(G, 3, 2)
DFS(G, 0)
BFS(G)
}