数据结构面试题

本文详细讲解了数组和链表的区别、哈希表的工作原理、二叉树的概念、深度优先搜索和广度优先搜索的实现,以及堆、栈、链表的特性。还涉及到了检测链表循环、红黑树、二叉搜索树、树与图的区别,以及链表反转、查找重复元素和最长不重复子串等问题,以及如何高效地对链表进行排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.解释数组和链表的区别。

答:主要区别在于内存分布结构和元素访问方式。数组在内存中是连续的,访问元素的时间复杂度为O(1);但是插入和删除元素需要整个数组移动,时间复杂度为O(n)。链表在内存中是离散的,插入和删除元素的时间复杂度为O(1);但是访问元素需要从头节点开始遍历,时间复杂度为O(n)。

2.什么是哈希表?它的工作原理是什么?

答:哈希表是一种实现了关联数组的抽象的数据类型,也就是说,它可以通过键映射到值。哈希表的工作原理是使用一个哈希函数将键转换为索引,然后在索引位置存储值。

3.什么是二叉树?

答:二叉树是一种树形结构,其中每个节点最多包含两个子节点(通常被称为左子节点和右子节点)。

示例代码:

   class Node {
       int key;
       Node left, right;
   
       public Node(int item) {
           key = item;
           left = right = null;
       }
   }
   class BinaryTree {
       Node root;
   
       BinaryTree(int key) {
           root = new Node(key);
       }
   
       BinaryTree() {
           root = null;
       }
   }

4.描述如何实现深度优先搜索和广度优先搜索。

答:在二叉树中,深度优先搜索包括:先序遍历、中序遍历和后序遍历。广度优先搜索即层次遍历。

示例代码:

   void printPreorder(Node node) {
       if (node == null)
           return;
       System.out.print(node.key + " ");
       printPreorder(node.left);
       printPreorder(node.right);
   }
   
   void printInorder(Node node) {
       if (node == null)
           return;
       printInorder(node.left);
       System.out.print(node.key + " ");
       printInorder(node.right);
   }
   
   void printPostorder(Node node) {
       if (node == null)
           return;
       printPostorder(node.left);
       printPostorder(node.right);
       System.out.print(node.key + " ");
   }

   void printLevelOrder() {
       int h = height(root);
       int i;
       for (i = 1; i <= h; i++)
           printGivenLevel(root, i);
   }

   void printGivenLevel(Node root, int level) {
    if (root == null)
            return;
        if (level == 1)
            System.out.print(root.key + " ");
        else if (level > 1) {
            printGivenLevel(root.left, level - 1);
            printGivenLevel(root.right, level - 1);
        }
}

5.你能解释一下堆和栈吗?

答:堆和栈都是一种数据存储结构。堆是动态分配的,大小不固定,可以随时变化。而栈是LIFO(后进先出)的数据结构,有固定的大小,不能动态扩展。

6.如何检测链表中的循环?

答:我们可以使用两个指针,一个快指针和一个慢指针。如果在链表中有循环,那么这两个指针最终会相遇。

class Node {
    int num;
    Node next;
}

boolean hasCycle(Node head) {
    if (head==null || head.next==null) return false;
    Node slow = head;
    Node fast = head.next;
    while(slow != fast) {
        if(fast==null || fast.next==null) return false;
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
}

7.什么是红黑树?

答:红黑树是一种自平衡的二叉搜索树,每个节点都带有一个颜色属性,为红色或黑色。在树的每次修改后,都会通过颜色更改和旋转来自动调整,以达到平衡。由于涉及到的代码太多,这里暂时不提供Java代码示例。

8.如何实现二叉搜索树?

class Node {
    int value;
    Node left;
    Node right;

    Node(int value) {
        this.value = value;
        left = null;
        right = null;
    }
}

class BinarySearchTree {
    Node root;

    void insert(int value) {
        root = insertRec(root, value);
    }

    Node insertRec(Node root, int value) {

        if (root == null) {
            root = new Node(value);
            return root;
        }

        if (value < root.value)
            root.left = insertRec(root.left, value);
        else if (value > root.value)
            root.right = insertRec(root.right, value);

        return root;
    }
}

9.解释树、图和链表的区别。

答:树是一种分层的数据结构,具有一个顶部节点(根),每个节点具有零个或多个子节点,并且没有节点具有两个父节点。

图是一种更复杂的数据结构,可以通过边将任何两个节点连接起来,图可以是连通的也可以是非连通的,并且边可以有方向也可以无方向。

链表是最简单的数据结构之一,它是一系列节点的集合,每个节点都有指向下一个节点的指针。

10.如何反转链表?

class Node {
    int value;
    Node next;
 }

Node reverse(Node node) {
    Node previous = null;
    Node current = node;
    Node forward = null;
    while (current != null) {
        forward = current.next;
        current.next = previous;
        previous = current;
        current = forward;
    }
    node = previous;
    return node;
}

11.如何查找数组中的重复元素?

答案:可以使用HashSet来找到数组中的重复元素。HashSet是一个不包含重复元素的集合。

示例代码:

import java.util.HashSet;

void printDuplicates(int[] arr) {
    HashSet<Integer> set = new HashSet<>();
    for(int i: arr) {
        if(set.contains(i)) {
            System.out.println(i);
        } else {
            set.add(i);
        }
    }
}

12.如何查找字符串中最长的不重复子字符串?

答案:这是一个经典的滑动窗口问题,我们可以使用HashSet来做一个滑动窗口,从左到右扫描字符串。

示例代码

import java.util.HashSet;

int lengthOfLongestSubstring(String s) {
    int n = s.length();
    HashSet<Character> set = new HashSet<>();
    int ans = 0, i = 0, j = 0;
    while (i < n && j < n) {
        if (!set.contains(s.charAt(j))) {
            set.add(s.charAt(j++));
            ans = Math.max(ans, j - i);
        } else {
            set.remove(s.charAt(i++));
        }
    }
    return ans;
}

13.如何有效地对链表进行排序?

答案:合并排序是一种被广泛应用于链表的排序算法,它的平均和最差时间复杂度为线性对数级。

示例代码:

class Node {
    int value;
    Node next;

    Node(int value) {
        this.value = value;
    }
}

Node sortedMerge(Node a, Node b) {
    Node result = null;
    if (a == null)
        return b;
    else if (b == null)
        return a;

    if (a.value <= b.value) {
        result = a;
        result.next = sortedMerge(a.next, b);
    } else {
        result = b;
        result.next = sortedMerge(a, b.next);
    }
    return result;
}

Node mergeSort(Node h) {
    if (h == null || h.next == null) {
        return h;
    }

    Node middle = getMiddle(h);
    Node nextOfMiddle = middle.next;

    middle.next = null;

    Node left = mergeSort(h);

    Node right = mergeSort(nextOfMiddle);

    Node sortedList = sortedMerge(left, right);
    return sortedList;
}

Node getMiddle(Node h) {
    if (h == null)
        return h;
    Node fastptr = h.next;
    Node slowptr = h;

    while (fastptr != null) {
        fastptr = fastptr.next;
        if(fastptr != null) {
            slowptr = slowptr.next;
            fastptr = fastptr.next;
        }
    }
    return slowptr;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哎 你看

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值