数据结构与算法面试题汇总(Go 语言版)

目录

数据结构与算法面试题汇总(Go 语言版)

一、排序算法

(一)快速排序

(二)归并排序

(三)冒泡排序

(四)插入排序

二、数据结构实现

(一)LRU 缓存

(二)哈希表

(三)基于数组的栈

(四)基于优先队列的最小堆

三、链表操作

(一)反转单链表

(二)找出两个单链表的交点

(三)检测单链表中是否有环

四、树的操作

(一)判断二叉树是否平衡

(二)二叉树的遍历

(三)二叉树的层序遍历

(四)二叉搜索树、平衡二叉树、红黑树、哈夫曼树、前缀树的理解


一、排序算法

(一)快速排序

快速排序是一种高效的排序算法,它采用分治的思想,通过选择一个基准元素,将数组分为两部分,小于基准元素的部分和大于基准元素的部分,然后分别对这两部分进行递归排序。

package main

func quickSort(arr []int) []int {
    if len(arr) <= 1 {
        return arr
    }
    pivot := arr[len(arr)-1]
    left := []int{}
    right := []int{}
    for _, v := range arr[:len(arr)-1] {
        if v < pivot {
            left = append(left, v)
        } else {
            right = append(right, v)
        }
    }
    return append(append(quickSort(left), pivot), quickSort(right)...)
}

(二)归并排序

归并排序也是一种分治算法,它将数组分成两个子数组,分别对两个子数组进行排序,然后将两个有序的子数组合并成一个有序的数组。

package main

func mergeSort(arr []int) []int {
    if len(arr) <= 1 {
        return arr
    }
    mid := len(arr) / 2
    left := mergeSort(arr[:mid])
    right := mergeSort(arr[mid:])
    return merge(left, right)
}

func merge(left, right []int) []int {
    result := []int{}
    i, j := 0, 0
    for i < len(left) && j < len(right) {
        if left[i] < right[j] {
            result = append(result, left[i])
            i++
        } else {
            result = append(result, right[j])
            j++
        }
    }
    result = append(result, left[i:]...)
    result = append(result, right[j:]...)
    return result
}

(三)冒泡排序

冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。

package main

func bubbleSort(arr []int) []int {
    n := len(arr)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if arr[j] > arr[j+1] {
                arr[j], arr[j+1] = arr[j+1], arr[j]
            }
        }
    }
    return arr
}

(四)插入排序

插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

package main

func insertionSort(arr []int) []int {
    n := len(arr)
    for i := 1; i < n; i++ {
        key := arr[i]
        j := i - 1
        for j >= 0 && arr[j] > key {
            arr[j+1] = arr[j]
            j--
        }
        arr[j+1] = key
    }
    return arr
}

二、数据结构实现

(一)LRU 缓存

LRU(Least Recently Used)缓存是一种最近最少使用的缓存淘汰策略。当缓存满时,会淘汰最近最少使用的元素。

package main

import "container/list"

type LRUCache struct {
    capacity int
    list     *list.List
    items    map[int]*list.Element
}

type pair struct {
    key   int
    value int
}

func NewLRUCache(capacity int) *LRUCache {
    return &LRUCache{
        capacity: capacity,
        list:     list.New(),
        items:    make(map[int]*list.Element),
    }
}

func (c *LRUCache) Get(key int) int {
    if elem, ok := c.items[key]; ok {
        c.list.MoveToFront(elem)
        return elem.Value.(*pair).value
    }
    return -1
}

func (c *LRUCache) Put(key int, value int) {
    if elem, ok := c.items[key]; ok {
        c.list.MoveToFront(elem)
        elem.Value.(*pair).value = value
    } else {
        if len(c.items) >= c.capacity {
            lastElem := c.list.Back()
            delete(c.items, lastElem.Value.(*pair).key)
            c.list.Remove(lastElem)
        }
        newElem := c.list.PushFront(&pair{key, value})
        c.items[key] = newElem
    }
}

(二)哈希表

哈希表是一种根据关键码值(Key value)而直接进行访问的数据结构。

package main

type HashTable struct {
    buckets []*list.List
    size    int
}

type entry struct {
    key   string
    value interface{}
}

func NewHashTable(size int) *HashTable {
    ht := &HashTable{
        buckets: make([]*list.List, size),
        size:    size,
    }
    for i := range ht.buckets {
        ht.buckets[i] = list.New()
    }
    return ht
}

func (h *HashTable) hash(key string) int {
    hash := 0
    for _, char := range key {
        hash += int(char)
    }
    return hash % h.size
}

func (h *HashTable) Put(key string, value interface{}) {
    index := h.hash(key)
    list := h.buckets[index]
    for e := list.Front(); e!= nil; e = e.Next() {
        if e.Value.(*entry).key == key {
            e.Value.(*entry).value = value
            return
        }
    }
    list.PushBack(&entry{key, value})
}

func (h *HashTable) Get(key string) interface{} {
    index := h.hash(key)
    list := h.buckets[index]
    for e := list.Front(); e!= nil; e = e.Next() {
        if e.Value.(*entry).key == key {
            return e.Value.(*entry).value
        }
    }
    return nil
}

(三)基于数组的栈

栈是一种后进先出(LIFO)的数据结构。

package main

type ArrayStack struct {
    items []interface{}
    top   int
}

func NewArrayStack(capacity int) *ArrayStack {
    return &ArrayStack{
        items: make([]interface{}, capacity),
        top:   -1,
    }
}

func (s *ArrayStack) Push(item interface{}) {
    if s.top == len(s.items)-1 {
        panic("Stack is full")
    }
    s.top++
    s.items[s.top] = item
}

func (s *ArrayStack) Pop() interface{} {
    if s.top == -1 {
        panic("Stack is empty")
    }
    item := s.items[s.top]
    s.top--
    return item
}

func (s *ArrayStack) IsEmpty() bool {
    return s.top == -1
}

(四)基于优先队列的最小堆

最小堆是一种完全二叉树,每个节点的值都小于或等于其子节点的值。

package main

type MinHeap struct {
    items []int
}

func NewMinHeap() *MinHeap {
    return &MinHeap{}
}

func (h *MinHeap) Insert(item int) {
    h.items = append(h.items, item)
    h.heapifyUp(len(h.items) - 1)
}

func (h *MinHeap) ExtractMin() int {
    if len(h.items) == 0 {
        return -1
    }
    min := h.items[0]
    h.items[0] = h.items[len(h.items)-1]
    h.items = h.items[:len(h.items)-1]
    h.heapifyDown(0)
    return min
}

func (h *MinHeap) heapifyUp(index int) {
    for index > 0 {
        parentIndex := (index - 1) / 2
        if h.items[parentIndex] <= h.items[index] {
            break
        }
        h.items[parentIndex], h.items[index] = h.items[index], h.items[parentIndex]
        index = parentIndex
    }
}

func (h *MinHeap) heapifyDown(index int) {
    for {
        leftChildIndex := 2*index + 1
        rightChildIndex := 2*index + 2
        smallest := index
        if leftChildIndex < len(h.items) && h.items[leftChildIndex] < h.items[smallest] {
            smallest = leftChildIndex
        }
        if rightChildIndex < len(h.items) && h.items[rightChildIndex] < h.items[smallest] {
            smallest = rightChildIndex
        }
        if smallest == index {
            break
        }
        h.items[index], h.items[smallest] = h.items[smallest], h.items[index]
        index = smallest
    }
}

三、链表操作

(一)反转单链表

反转一个单链表,即将链表的节点顺序反转。

package main

type ListNode struct {
    Val  int
    Next *ListNode
}

func reverseList(head *ListNode) *ListNode {
    var prev *ListNode
    curr := head
    for curr!= nil {
        next := curr.Next
        curr.Next = prev
        prev = curr
        curr = next
    }
    return prev
}

(二)找出两个单链表的交点

给定两个单链表,判断它们是否有交点,如果有,返回交点的节点。

package main

func getIntersectionNode(headA, headB *ListNode) *ListNode {
    if headA == nil || headB == nil {
        return nil
    }
    pA, pB := headA, headB
    for pA!= pB {
        if pA == nil {
            pA = headB
        } else {
            pA = pA.Next
        }
        if pB == nil {
            pB = headA
        } else {
            pB = pB.Next
        }
    }
    return pA
}

(三)检测单链表中是否有环

判断一个单链表中是否存在环。

package main

func hasCycle(head *ListNode) bool {
    if head == nil {
        return false
    }
    slow, fast := head, head
    for fast!= nil && fast.Next!= nil {
        slow = slow.Next
        fast = fast.Next.Next
        if slow == fast {
            return true
        }
    }
    return false
}

四、树的操作

(一)判断二叉树是否平衡

判断一棵二叉树是否是平衡二叉树,即该二叉树的任意节点的左右子树高度差不超过 1。

package main

type TreeNode struct {
    Val   int
    Left  *TreeNode
    Right *TreeNode
}

func isBalanced(root *TreeNode) bool {
    return height(root)!= -1
}

func height(node *TreeNode) int {
    if node == nil {
        return 0
    }
    leftHeight := height(node.Left)
    if leftHeight == -1 {
        return -1
    }
    rightHeight := height(node.Right)
    if rightHeight == -1 {
        return -1
    }
    if abs(leftHeight-rightHeight) > 1 {
        return -1
    }
    return max(leftHeight, rightHeight) + 1
}

func abs(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

(二)二叉树的遍历

  1. 前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树。
  2. 中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树。
  3. 后序遍历:先遍历左子树,然后遍历右子树,最后访问根节点。

package main

func preOrderTraversal(root *TreeNode) []int {
    result := []int{}
    if root == nil {
        return result
    }
    result = append(result, root.Val)
    result = append(result, preOrderTraversal(root.Left)...)
    result = append(result, preOrderTraversal(root.Right)...)
    return result
}

func inOrderTraversal(root *TreeNode) []int {
    result := []int{}
    if root == nil {
        return result
    }
    result = append(result, inOrderTraversal(root.Left)...)
    result = append(result, root.Val)
    result = append(result, inOrderTraversal(root.Right)...)
    return result
}

func postOrderTraversal(root *TreeNode) []int {
    result := []int{}
    if root == nil {
        return result
    }
    result = append(result, postOrderTraversal(root.Left)...)
    result = append(result, postOrderTraversal(root.Right)...)
    result = append(result, root.Val)
    return result
}

(三)二叉树的层序遍历

按层次遍历二叉树,即从根节点开始,依次遍历每一层的节点。

package main

func levelOrder(root *TreeNode) [][]int {
    result := [][]int{}
    if root == nil {
        return result
    }
    queue := []*TreeNode{root}
    for len(queue) > 0 {
        levelSize := len(queue)
        levelValues := []int{}
        for i := 0; i < levelSize; i++ {
            node := queue[0]
            queue = queue[1:]
            levelValues = append(levelValues, node.Val)
            if node.Left!= nil {
                queue = append(queue, node.Left)
            }
            if node.Right!= nil {
                queue = append(queue, node.Right)
            }
        }
        result = append(result, levelValues)
    }
    return result
}

(四)二叉搜索树、平衡二叉树、红黑树、哈夫曼树、前缀树的理解

  1. 二叉搜索树

    • 定义:二叉搜索树是一种特殊的二叉树,对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。
    • 特点:
      • 中序遍历二叉搜索树可以得到一个有序的序列。
      • 查找、插入和删除操作的时间复杂度在平均情况下为 O (log n),但在最坏情况下可能会退化为 O (n),当二叉搜索树退化为一条链时就会出现这种情况。
    • 用途:常用于实现动态集合的数据结构,如集合的插入、删除、查找等操作。
     

    以下是用 Go 语言实现一个简单的二叉搜索树:

   package main

   type BinarySearchTree struct {
       root *TreeNode
   }

   type TreeNode struct {
       Val   int
       Left  *TreeNode
       Right *TreeNode
   }

   func (bst *BinarySearchTree) Insert(val int) {
       bst.root = insert(bst.root, val)
   }

   func insert(node *TreeNode, val int) *TreeNode {
       if node == nil {
           return &TreeNode{Val: val}
       }
       if val < node.Val {
           node.Left = insert(node.Left, val)
       } else if val > node.Val {
           node.Right = insert(node.Right, val)
       }
       return node
   }

   func (bst *BinarySearchTree) Search(val int) bool {
       return search(bst.root, val)
   }

   func search(node *TreeNode, val int) bool {
       if node == nil {
           return false
       }
       if val == node.Val {
           return true
       } else if val < node.Val {
           return search(node.Left, val)
       } else {
           return search(node.Right, val)
       }
   }

平衡二叉树(AVL 树)

定义:平衡二叉树是一种特殊的二叉树,它的左右子树的高度差不超过 1。

特点:

  • 保持树的平衡可以确保查找、插入和删除操作的时间复杂度始终保持在 O (log n)。
  • 通过旋转操作(左旋、右旋)来维持平衡。

用途:在需要高效进行查找、插入和删除操作,并且数据量较大的情况下使用。

以下是用 Go 语言实现的 AVL 树代码:

package main

type AVLTreeNode struct {
    Val   int
    Left  *AVLTreeNode
    Right *AVLTreeNode
    Height int
}

func NewAVLTreeNode(val int) *AVLTreeNode {
    return &AVLTreeNode{Val: val, Height: 1}
}

func height(node *AVLTreeNode) int {
    if node == nil {
        return 0
    }
    return node.Height
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func getBalance(node *AVLTreeNode) int {
    if node == nil {
        return 0
    }
    return height(node.Left) - height(node.Right)
}

func rightRotate(y *AVLTreeNode) *AVLTreeNode {
    x := y.Left
    T2 := x.Right

    x.Right = y
    y.Left = T2

    y.Height = max(height(y.Left), height(y.Right)) + 1
    x.Height = max(height(x.Left), height(x.Right)) + 1

    return x
}

func leftRotate(x *AVLTreeNode) *AVLTreeNode {
    y := x.Right
    T2 := y.Left

    y.Left = x
    x.Right = T2

    x.Height = max(height(x.Left), height(x.Right)) + 1
    y.Height = max(height(y.Left), height(y.Right)) + 1

    return y
}

func insert(node *AVLTreeNode, val int) *AVLTreeNode {
    if node == nil {
        return NewAVLTreeNode(val)
    }

    if val < node.Val {
        node.Left = insert(node.Left, val)
    } else if val > node.Val {
        node.Right = insert(node.Right, val)
    } else {
        return node
    }

    node.Height = max(height(node.Left), height(node.Right)) + 1

    balance := getBalance(node)

    if balance > 1 && val < node.Left.Val {
        return rightRotate(node)
    }

    if balance < -1 && val > node.Right.Val {
        return leftRotate(node)
    }

    if balance > 1 && val > node.Left.Val {
        node.Left = leftRotate(node.Left)
        return rightRotate(node)
    }

    if balance < -1 && val < node.Right.Val {
        node.Right = rightRotate(node.Right)
        return leftRotate(node)
    }

    return node
}

type AVLTree struct {
    root *AVLTreeNode
}

func NewAVLTree() *AVLTree {
    return &AVLTree{}
}

func (t *AVLTree) Insert(val int) {
    t.root = insert(t.root, val)
}

红黑树

定义:红黑树是一种自平衡的二叉搜索树,每个节点都带有颜色属性(红色或黑色),并满足一定的性质。

特点:

  • 从根节点到叶子节点的最长路径不超过最短路径的两倍。
  • 插入和删除操作可能会导致树的不平衡,但可以通过旋转和重新着色等操作来保持树的平衡。

用途:广泛应用于各种需要高效查找、插入和删除操作的场景,如 C++ 的 STL 中的关联容器、Java 的 TreeMap 和 TreeSet 等。

以下是用 Go 语言实现的红黑树代码:

package main

type Color bool

const (
    Red   Color = true
    Black Color = false
)

type RBTreeNode struct {
    Val   int
    Left  *RBTreeNode
    Right *RBTreeNode
    Color Color
}

func NewRBTreeNode(val int) *RBTreeNode {
    return &RBTreeNode{Val: val, Color: Red}
}

func isRed(node *RBTreeNode) bool {
    if node == nil {
        return false
    }
    return node.Color == Red
}

func rotateLeft(h *RBTreeNode) *RBTreeNode {
    x := h.Right
    h.Right = x.Left
    x.Left = h
    x.Color = h.Color
    h.Color = Red
    return x
}

func rotateRight(h *RBTreeNode) *RBTreeNode {
    x := h.Left
    h.Left = x.Right
    x.Right = h
    x.Color = h.Color
    h.Color = Red
    return x
}

func flipColors(h *RBTreeNode) {
    h.Color = Red
    h.Left.Color = Black
    h.Right.Color = Black
}

func insert(root *RBTreeNode, val int) *RBTreeNode {
    if root == nil {
        return NewRBTreeNode(val)
    }

    if val < root.Val {
        root.Left = insert(root.Left, val)
    } else if val > root.Val {
        root.Right = insert(root.Right, val)
    } else {
        return root
    }

    if isRed(root.Right) &&!isRed(root.Left) {
        root = rotateLeft(root)
    }
    if isRed(root.Left) && isRed(root.Left.Left) {
        root = rotateRight(root)
    }
    if isRed(root.Left) && isRed(root.Right) {
        flipColors(root)
    }

    return root
}

type RBTree struct {
    root *RBTreeNode
}

func NewRBTree() *RBTree {
    return &RBTree{}
}

func (t *RBTree) Insert(val int) {
    t.root = insert(t.root, val)
    t.root.Color = Black
}

 

1.栈和队列的共同特点是(只允许在端点处插入和删除元素) 4.栈通常采用的两种存储结构是(线性存储结构和链表存储结构) 5.下列关于栈的叙述正确的是(D) A.栈是非线性结构B.栈是一种树状结构C.栈具有先进先出的特征D.栈有后进先出的特征 6.链表不具有的特点是(B)A.不必事先估计存储空间 B.可随机访问任一元素 C.插入删除不需要移动元素 D.所需空间与线性表长度成正比 7.用链表表示线性表的优点是(便于插入和删除操作) 8.在单链表中,增加头结点的目的是(方便运算的实现) 9.循环链表的主要优点是(从表中任一结点出发都能访问到整个链表) 10.线性表L=(a1,a2,a3,……ai,……an),下列说法正确的是(D) A.每个元素都有一个直接前件和直接后件 B.线性表中至少要有一个元素 C.表中诸元素的排列顺序必须是由小到大或由大到小 D.除第一个和最后一个元素外,其余每个元素都有一个且只有一个直接前件和直接后件 11.线性表若采用链式存储结构时,要求内存中可用存储单元的地址(D) A.必须是连续的 B.部分地址必须是连续的C.一定是不连续的 D.连续不连续都可以 12.线性表的顺序存储结构和线性表的链式存储结构分别是(随机存取的存储结构、顺序存取的存储结构) 13.树是结点的集合,它的根结点数目是(有且只有1) 14.在深度为5的满二叉树中,叶子结点的个数为(31) 15.具有3个结点的二叉树有(5种形态) 16.设一棵二叉树中有3个叶子结点,有8个度为1的结点,则该二叉树中总的结点数为(13) 17.已知二叉树后序遍历序列是dabec,中序遍历序列是debac,它的前序遍历序列是(cedba) 18.已知一棵二叉树前序遍历和中序遍历分别为ABDEGCFH和DBGEACHF,则该二叉树的后序遍历为(DGEBHFCA) 19.若某二叉树的前序遍历访问顺序是abdgcefh,中序遍历访问顺序是dgbaechf,则其后序遍历的结点访问顺序是(gdbehfca) 20.数据库保护分为:安全性控制、 完整性控制 、并发性控制和数据的恢复。 1. 在计算机中,算法是指(解题方案的准确而完整的描述) 2.在下列选项中,哪个不是一个算法一般应该具有的基本特征(无穷性) 说明:算法的四个基本特征是:可行性、确定性、有穷性和拥有足够的情报。 3. 算法一般都可以用哪几种控制结构组合而成(顺序、选择、循环) 4.算法的时间复杂度是指(算法执行过程中所需要的基本运算次数) 5. 算法的空间复杂度是指(执行过程中所需要的存储空间) 6. 算法分析的目的是(分析算法的效率以求改进) ............ .................
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值