> 如果一个二维数组非常大,并且大部分都是默认值的情况下,我们就认为很稀疏。
> 所以我们可以将二维数据中有数据的坐标和值记录下来,保存在数据结构里,这就称之为稀疏数组!
2. 案例演示
我们以一个6*5的二维数组为例子
结构体
type spare struct {
row int // 行
col int // 列
val int // 值
}
数组(golang数组默认值都是0)
arr := [6][5]int{}
// 不同坐标设置默认值
arr[4][2] = 2
arr[4][4] = 3
arr[2][2] = 1
arr[5][2] = 2
存储数据
func saveSpaceArr(arr []int) []spare {
spareArr := []spare{}
// 遍历数组中所有不为0的元素
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
if arr[i][j] != 0 {
// 记录坐标和值
space := spare{
col: j,
row: i,
val: arr[i][j],
}
spareArr = append(spareArr, space)
}
}
}
return spareArr
}
读取数据
func readSpaceArr(spareArr []spare) []int{
arr = [6][5]int{}
for i := 0; i < len(spareArr); i++ {
// 读取行列值
row := spareArr[i].row
col := spareArr[i].col
val := spareArr[i].val
arr[row][col] = val
}
return arr
}
二、队列
我们这里使用数组来模拟
1. 单向队列队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除 *** 作,而在表的后端(rear)进行插入 *** 作。进行插入 *** 作的端称为队尾,进行删除 *** 作的端称为队头
结构体1、起始位置
2、添加一个元素
3、添加满元素时
4、删除元素
5、队列满员
type query struct {
arr [5]int
// 最大长度
maxSize int
// 首指针(不包含首位)
front int
// 尾指针
real int
}
增加、删除、判断满或者空的方法// 是否为空?
func (this *query) isNil() bool {
return (this.front == this.real)
}
// 是否为以满?
func (this *query) isFull() bool {
return (this.maxSize - 1 == this.real)
}
// 插入数据
func (this *query) push(val int) error {
if isFull(){
return errors.New("队列以满")
}
this.real++
this.arr[this.real] = val
return nil
}
// d出数据
func (this *query) pop() (int, error) {
if isNil(){
return -9999, errors.New("队列以空")
}
popVal := this.arr[this.front]
this.front++
return popVal, nil
}
// 显示数据
func (this *query) show(){
if isNil(){
fmt.Println([]int{})
}
// 注意此处this.front要加1,this.front指向的是d出的值
for i:=this.front+1; i<=this.real;i++ {
fmt.Println(this.arr[i])
}
}
初始化队列func main(){
query := &query{
maxSize: 5,
front: -1,
real: -1,
}
query.push(11)
query.push(22)
query.push(33)
query.push(44)
// 先进先出
val, err := query.pop()
if err != nil {
fmt.Println(err.Error())
}
// 显示数据
query.show()
}
2. 环形队列
结构体环形队列,故名思议,是一个环状,相对于单向队列来说是一个升级(单向列表一次性添加数据添加满了再删除无法继续使用队列),环形队列可以循环利用队列,只要队列有空余的位置就可以添加新的元素。
1、满员队列
2、删除元素
3、环形增加节点
4、环形满员状态
type circleQuery struct {
maxlen int
arr [5]int
// head包含队首元素,tail比队尾元素多1位
head, tail int
}
func (this circleQuery) String() string {
str, _ := json.Marshal(this)
return string(str)
}
增、删、查看个数等 *** 作// 是否为空
func (this *circleQuery) isNil() bool {
return (this.head == this.tail)
}
// 是否以满
func (this *circleQuery) isFull() bool {
return ( (this.tail + 1) % this.maxLen == this.head )
}
// 队列元素个数
func (this *circleQuery) count() int {
return ( (this.maxlen + this.tail - this.head) % this.maxlen )
}
// 添加元素
func (this *circleQuery) push(newVal int) {
if isFull() {
return
}
this.arr[this.tail] = newVal
this.tail = (this.tail + 1) % this.maxLen
}
func (this *circleQuery) pop() int {
if isNil() {
return
}
popVal := this.arr[this.head]
this.head = (this.head + 1) % this.maxLen
return popVal
}
初始化环形队列func main(){
cq := circleQuery{
head: 0,
tail: 0,
maxlen: 5,
}
cq.push(11)
cq.push(22)
cq.push(33)
cq.push(44)
fmt.Println(cq.pop())
cq.push(55)
cq.show()
}
三、链表
1、单向链表
1、初始状态
2、添加元素的状态
准备结构体
type linkListNode struct {
No int
Name string
Next *linkListNode // 指向下一个节点
}
// json序列化结构体
func (l *linkListNode) String() string {
str, _ := json.Marshal(l)
return string(str)
}
添加、删除、显示节点
// 插入节点
func insert(head *linkListNode, new *linkListNode){
temp := head
// 先找到最后一个节点,将新值插入到最后
for {
// 当节点Next为nil代表后续没有节点,直接插入即可
if temp.Next == nil {
temp.Next = new
break
}
temp = temp.Next
}
}
// 有序差入节点 (此方法按照No的大小排序)
func orderInsert(head *linkListNode, new *linkListNode){
temp := head
for {
if temp.Next == nil {
temp.Next = new
break
} else if temp.Next.No > new.No {
new.Next = tmep.Next
tmep.Next = new
break
}
temp = temp.Next
}
}
// 删除节点
func delete(head *linkListNode, no int) {
temp := head
for {
if temp.Next == nil {
fmt.Println("未找到该节点")
break
} else if temp.Next.No == no {
temp.Next = temp.Next.Next
break
}
temp = temp.Next
}
}
// 显示节点
func show(head *linkListNode){
temp := head
for {
if temp.Next != nil {
temp = temp.Next
fmt.Println(temp.String())
} else {
break
}
}
}
初始化节点
func main(){
// 头节点,默认为空
head := linkListNode{}
fmt.Println(1)
realHead1 := linkListNode{
No: 3,
Name: "张三",
Nickname: "傻逼",
}
//insert(&head, &realHead1)
sortInsert(&head, &realHead1)
fmt.Println(2)
realHead2 := linkListNode{
No: 4,
Name: "李四",
Nickname: "呆子",
}
//insert(&head, &realHead2)
sortInsert(&head, &realHead2)
realHead3 := linkListNode{
No: 1,
Name: "赵一",
Nickname: "傻逼",
}
//insert(&head, &realHead1)
sortInsert(&head, &realHead3)
delete(&head, 3)
show(&head)
}
2、双向链表
3、环型链表双向链表相对于单链表多了一个前指针,指向了上一个元素
// 环形列表
type circleLinkList struct {
No int
Name string
next *circleLinkList
}
func (c *circleLinkList) String() string {
str,_ := json.Marshal(c)
return string(str)
}
func insertCircleLinkList(head *circleLinkList, new *circleLinkList) {
// 判断是否是第一支猫
if head.next == nil {
head.No = new.No
head.Name = new.Name
head.next = head
return
}
// 定义一个临时变量,找到最后一个节点
temp := head
for true {
if temp.next == head {
temp.next = new
new.next = head
break
}
temp = temp.next
}
}
func deleteCircleLinkList(head *circleLinkList, no int) *circleLinkList {
if head.next == nil {
fmt.Println("空链表")
return head
}
temp := head
// 环形链表只有一个节点的情况
if temp.next == head {
temp.next = nil
return head
}
for true {
if temp.next.No == no {
if temp.next == head {
head = temp.next.next
}
temp.next = temp.next.next
}
if temp.next == head {
break
}
temp = temp.next
}
return head
}
func showCircleLinkList(head *circleLinkList) {
if head.next == nil {
fmt.Println("链表为空")
return
}
temp := head
for true {
fmt.Println(temp)
if temp.next == head {
break
}
temp = temp.next
}
}
func main() {
head := &circleLinkList{}
new1 := &circleLinkList{
No: 1,
Name: "new1",
}
new2 := &circleLinkList{
No: 2,
Name: "new2",
}
new3 := &circleLinkList{
No: 3,
Name: "new3",
}
new4 := &circleLinkList{
No: 4,
Name: "new4",
}
insertCircleLinkList(head, new1)
insertCircleLinkList(head, new2)
insertCircleLinkList(head, new3)
insertCircleLinkList(head, new4)
head = deleteCircleLinkList(head, 1)
showCircleLinkList(head)
}
4、环型链表问题:约瑟夫环
丢手帕
设编号为1,2,n个人围坐一圈,约定编号为K(1=
type dsp struct {
Id int
Name string
next *dsp
}
func (d *dsp) String() string {
str, _ := json.Marshal(d)
return string(str)
}
func insertDsp(head *dsp, new *dsp) {
if head.next == nil {
head.Name = new.Name
head.Id = new.Id
head.next = head
return
}
temp := head
for true {
if temp.next == head {
temp.next = new
new.next = head
break
}
temp = temp.next
}
}
func showDsp(head *dsp) {
if head.next == nil {
fmt.Println("空链表")
return
}
temp := head
for true {
if temp.next == head {
break
}
temp = temp.next
}
}
func deleteDsp(head *dsp, k int, m int) (*dsp, int) {
if head.next == nil {
fmt.Println("空链表")
return head, -1
}
if head.next == head {
head.next = nil
return head, head.Id
}
flagTemp := -1
temp := head
flag := 1
flagK := 1
for true {
if flagK == k {
flag++
} else {
flagK++
}
if flag == m {
if temp.next == head {
head = temp.next.next
}
flagTemp = temp.next.Id
temp.next = temp.next.next
break
}
temp = temp.next
}
return head, flagTemp
}
func main() {
head := &dsp{}
n1 := &dsp{
Id: 1,
Name: "100",
}
n2 := &dsp{
Id: 2,
Name: "102",
}
n3 := &dsp{
Id: 3,
Name: "103",
}
n4 := &dsp{
Id: 4,
Name: "104",
}
insertDsp(head, n1)
insertDsp(head, n2)
insertDsp(head, n3)
insertDsp(head, n4)
showDsp(head)
query := []int{}
index := -1
k := 2
m := 3
for true {
head, index = deleteDsp(head, k, m)
if index != -1 {
fmt.Println(index)
query = append(query, index)
}
if head.next == nil {
break
}
}
fmt.Println(query)
}
5、链表问题:链表反转
链表反转的核心思想就是反转next的指针
使用两个指针作为临时指针
type link struct {
id int
next *link
}
func reversal(head *link) {
// 当前节点
cur := head
// 上一个节点
pre := nil
for {
if pre.next == nil {
break
}
// 暂存cur的下一个节点
temp := cur.next
// 将当前指针指针指向上一个节点
cur.next = pre
// 后移cur和pre两个指针
pre = cur
cur = temp
}
}
四、栈
数据结构1、初始化top指针下标为-1,同理top为-1表示栈为空
2、添加一个元素,top++
3、栈满时,top+1 = max
type stack struct {
max int // 最大存放个数
top int // 栈顶
arr [5]int
}
增删该查
// 入栈
func (s *stack) push(new int) {
if s.top == (s.max - 1) {
fmt.Println("栈以满")
return
}
s.top++
s.arr[s.top] = new
}
// 出栈
func (s *stack) pop() (int, error) {
if s.top == -1 {
fmt.Println("栈为空")
return -1, errors.New("空栈")
}
val := s.arr[s.top]
s.top--
return val, nil
}
// 显示栈内的数据
func (s *stack) showStack() {
if s.top == -1 {
return
}
for i := 0; i <= s.top; i++ {
fmt.Println(s.arr[i])
}
}
初始化
func ShowStack() {
stack := stack{
max: 5,
top: -1,
arr: [5]int{0, 0, 0, 0, 0},
}
// 先进后出
stack.push(2)
stack.push(1)
stack.push(4)
stack.push(9)
stack.push(10)
stack.pop() // 先出10
stack.pop() // 先出9
stack.showStack()
}
五、哈希散列表
代码实现 结构体所谓的散列表,就是将一组值,按照一定的规律,比如取模、取余等,分散在对应不同的链表中,查找只需要定位到对应的呢根链表中就可以加快查询速度。
比如,之前有100个数据,分为十组,每组就只有10个数据,这样,只需要从10个数据中查找,数据量越大,散列的优势就会大大加快了查询速度。
// 存放结构体head的数组
type hashTable struct {
Heads [7]*userLink
}
// 头节点
type userLink struct {
Head *userState
}
// 节点
type userState struct {
Id int
State byte
next *userState
}
添加节点
// 节点长度是7
// 依据 id 来判断,放入第几根根的链表中
func (this *hashTable) push(userState *userState) {
mod := userState.Id % 7
this.Heads[mod].insert(userState)
}
// userState 插入的新节点
func (this *userLink) insert(userState *userState) {
temp := this.Head
if temp.next == nil {
temp.next = userState
return
}
for true {
if temp.next == nil {
temp.next = userState
break
} else if temp.next.Id == userState.Id {
temp.next.State = userState.State
break
} else if temp.next.Id > userState.Id {
userState.next = temp.next
temp.next = userState
break
}
temp = temp.next
}
}
查找节点
func (this *hashTable) getState(id int) {
// 1. id 取模判断是那根节点
mod := id % 7
// 2. 获取对应节点的根节点
temp := this.Heads[mod].Head
// 3. 去对应的节点查找
for true {
if temp.next == nil {
fmt.Println("没有找到")
break
}
if temp.next.Id == id {
fmt.Println("找到了:", temp.next)
break
}
temp = temp.next
}
}
输出节点
func (this *hashTable) show() {
for i := 0; i < len(this.Heads); i++ {
temp := this.Heads[i].Head
fmt.Print("第",i,"个队列:")
for true {
if temp.next == nil {
break
}
fmt.Print(temp.next, " ")
temp = temp.next
}
fmt.Println()
}
}
六、二叉树
1、前序遍历构建如下结构的二叉树
初始化:type tree struct { id int left *tree right *tree } func ShowTree() { tree3 := &tree{ id: 3, } tree4 := &tree{ id: 4, } tree6 := &tree{ id: 6, } tree5 := &tree{ id: 5, left: tree6, } tree1 := &tree{ id: 1, left: tree5, } tree2 := &tree{ id: 2, left: tree3, right: tree4, } headTree := &tree{ id: 0, left: tree1, right: tree2, } // 前序遍历 headTree.Preface() }
遍历顺序:先根节点,再左边所有节点,再右边所有节点
func (this *tree) Preface() {
if this == nil {
return
}
fmt.Println("当前节点id:", this.id)
this.left.Preface()
this.right.Preface()
}
2、中序遍历
遍历顺序:先左边所有节点,再根节点,再右边所有节点
func (this *tree) MidOrder() {
if this == nil {
return
}
this.left.MidOrder()
fmt.Println("当前节点id:", this.id)
this.right.MidOrder()
}
3、后序遍历
遍历顺序:先左边所有节点,再右边所有节点,再根节点
func (this *tree) Postscript() {
if this == nil {
return
}
this.left.Postscript()
this.right.Postscript()
fmt.Println("当前节点id:", this.id)
}
七、排序
此处省略冒泡排序…
1. 选择排序这个排序就是冒泡的升级版本,顺序依次遍历数组,没有都将数组中最小/最大的元素遍历出来放在标志位。
func SelectSort(flag int, arr []int) {
if flag == len(arr) {
return
}
// 标记最小位置下标
tag := flag
for i := flag; i < len(arr); i++ {
if arr[i] < arr[tag] {
tag = i
}
}
arr[flag], arr[tag] = arr[tag], arr[flag]
// 递归遍历+1位后的数组
SelectSort(flag + 1, arr)
}
2. 插入排序
func InsertSort(arr []int){
for i:=1;i<len(arr);i++{
// 记录当前排序的值、下标
insertVal := arr[i]
insertIndex := i - 1
// 循环判断,如果满足条件就将insertIndex+1的值变为insertIndex的值
for insertIndex >= 0 && arr[insertIndex] > insertVal {
arr[insertIndex+1] = arr[insertIndex]
insertIndex--
}
// 因为最后满足条件时insertIndex--
// 若insertIndex + 1 == i则表示不满足条件无序置换
// 故insertIndex+1的下标才为当前需要插入的值
if insertIndex + 1 != i {
arr[insertIndex+1] = insertVal
}
}
}
3. 希尔排序
希尔排序其实也是一种插入排序,缩小增量的插入排序
希尔是将数组依次划分等间距的数组,对等间距数组进行排序
func ShellSort(arr []int){
// 1、划分为凉快数组
for i := len(arr)/2; i > 0;i /= 2 {
// 2、分别对两个数组进行插入排序
for j := i; j < len(arr);j += i {
insertVal := arr[j]
insertIndex := j - i
for insertIndex >= 0 && arr[insertIndex] > insertVal {
arr[insertIndex + i] = arr[insertIndex]
insertIndex -= i
}
if insertIndex + i != j {
arr[insertIndex + i] = insertVal
}
}
}
}
4. 快速排序
在我看来快速排序是效率最快的,也是最好理解的一种排序方式,写起来非常复合大脑的思考的结果,思路并不反人类
func QuickSort(arr []int, left, right int) {
if left > right {
return
}
i := left
j := right
// 将最左元素作为基准数
base := arr[left]
for i != j {
for arr[j] > base && i < j {
j--
}
for arr[i] < base && i < j {
i++
}
// 左右两边相交换
arr[i], arr[j] = arr[j], arr[i]
}
// 此时i、j的位置相交,左边比基准大,右边比基准小
// 第一轮循环结束,将基准与i、j相交的位置进行交换
arr[left], arr[i] = arr[i], arr[left]
// 先递归排序左边
QuickSort(arr, left, i-1)
// 再递归排序右边
QuickSort(arr, i+1, right)
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)