Interview[数据结构与算法]

286 阅读9分钟

数据结构与算法篇

1、常用的数据结构

原文:The top data structures you should know for your next coding interview

译者:Fundebug

我们首先列出最常用的数据结构

  • 数组
  • 堆栈
  • 队列
  • 链表
  • 字典树
  • 哈希表
1. 数组

**数组(Array)**大概是最简单,也是最常用的数据结构了。其他数据结构,比如栈和队列都是由数组衍生出来的。

下图展示了 1 个数组,它有 4 个元素:

img

每一个数组元素的位置由数字编号,称为下标或者索引(index)。大多数编程语言的数组第一个元素的下标是 0。

根据维度区分,有 2 种不同的数组:

  • 一维数组(如上图所示)
  • 多维数组(数组的元素为数组)

数组的基本操作

  • Insert - 在某个索引处插入元素
  • Get - 读取某个索引处的元素
  • Delete - 删除某个索引处的元素
  • Size - 获取数组的长度

常见数组代码面试题

2. 栈

撤回,即 Ctrl+Z,是我们最常见的操作之一,大多数应用都会支持这个功能。你知道它是怎么实现的吗?答案是这样的:把之前的应用状态(限制个数)保存到内存中,最近的状态放到第一个。这时,我们需要**栈(stack)**来实现这个功能。

栈中的元素采用 LIFO (Last In First Out),即后进先出

下图的栈有 3 个元素,3 在最上面,因此它会被第一个移除:

img

栈的基本操作

  • Push —  在栈的最上方插入元素
  • Pop — 返回栈最上方的元素,并将其删除
  • isEmpty —  查询栈是否为空
  • Top —  返回栈最上方的元素,并不删除

常见的栈代码面试题

3. 队列

队列(Queue)与栈类似,都是采用线性结构存储数据。它们的区别在于,栈采用 LIFO 方式,而队列采用先进先出,即FIFO(First in First Out)

下图展示了一个队列,1 是最上面的元素,它会被第一个移除:

img

队列的基本操作

  • Enqueue —  在队列末尾插入元素
  • Dequeue —  将队列第一个元素删除
  • isEmpty —  查询队列是否为空
  • Top —  返回队列的第一个元素

常见的队列代码面试题

4. 链表

**链表(Linked List)**也是线性结构,它与数组看起来非常像,但是它们的内存分配方式、内部结构和插入删除操作方式都不一样。

链表是一系列节点组成的链,每一个节点保存了数据以及指向下一个节点的指针。链表头指针指向第一个节点,如果链表为空,则头指针为空或者为 null。

链表可以用来实现文件系统、哈希表和邻接表。

下图展示了一个链表,它有 3 个节点:

img

链表分为 2 种:

  • 单向链表
  • 双向链表

链表的基本操作

  • InsertAtEnd —  在链表结尾插入元素
  • InsertAtHead —  在链表开头插入元素
  • Delete —  删除链表的指定元素
  • DeleteAtHead —  删除链表第一个元素
  • Search —  在链表中查询指定元素
  • isEmpty —  查询链表是否为空

常见的队列代码面试题

5. 图

图(graph)由多个节点(vertex)构成,节点之间阔以互相连接组成一个网络。(x, y)表示一条边(edge),它表示节点 x 与 y 相连。边可能会有权值(weight/cost)

img

图分为两种:

  • 无向图
  • 有向图

在编程语言中,图有可能有以下两种形式表示:

  • 邻接矩阵(Adjacency Matrix)
  • 邻接表(Adjacency List)

遍历图有两周算法

  • 广度优先搜索(Breadth First Search)
  • 深度优先搜索(Depth First Search)

常见的图代码面试题

6. 树

**树(Tree)**是一个分层的数据结构,由节点和连接节点的边组成。树是一种特殊的图,它与图最大的区别是没有循环。

树被广泛应用在人工智能和一些复杂算法中,用来提供高效的存储结构。

下图是一个简单的树以及与树相关的术语:

img

树有很多分类:

  • N 叉树(N-ary Tree)
  • 平衡树(Balanced Tree)
  • 二叉树(Binary Tree)
  • 二叉查找树(Binary Search Tree)
  • 平衡二叉树(AVL Tree)
  • 红黑树(Red Black Tree)
  • 2-3 树(2–3 Tree)

其中,二叉树和二叉查找树是最常用的树。

常见的树代码面试题

7. 前缀树

**前缀树(Prefix Trees 或者 Trie)**与树类似,用于处理字符串相关的问题时非常高效。它可以实现快速检索,常用于字典中的单词查询,搜索引擎的自动补全甚至 IP 路由。

下图展示了“top”, “thus”和“their”三个单词在前缀树中如何存储的:

img

单词是按照字母从上往下存储,“p”, “s”和“r”节点分别表示“top”, “thus”和“their”的单词结尾。

常见的树代码面试题

8. 哈希表

哈希(Hash)将某个对象变换为唯一标识符,该标识符通常用一个短的随机字母和数字组成的字符串来代表。哈希可以用来实现各种数据结构,其中最常用的就是哈希表(hash table)

哈希表通常由数组实现。

哈希表的性能取决于 3 个指标:

  • 哈希函数
  • 哈希表的大小
  • 哈希冲突处理方式

下图展示了有数组实现的哈希表,数组的下标即为哈希值,由哈希函数计算,作为哈希表的键(key),而数组中保存的数据即为值(value)

img

常见的哈希表代码面试题

2、 数据里有{1,2,3,4,5,6,7,8,9},请随机打乱顺序,生成一个新的数组(请以代码实现)

import java.util.Arrays;

//打乱数组
public class Demo1 {
    
    //随机打乱
    public static int[] srand(int[] a) {
        int[] b = new int[a.length];
        
        for(int i = 0; i < a.length;i++) {
            //随机获取下标
            int tmp = (int)(Math.random()*(a.length - i)); //随机数:[ 0 ,a.length - i )  
            b[i] = a[tmp];
            
            //将此时a[tmp]的下标移动到靠后的位置
            int change = a[a.length - i - 1];
            a[a.length - i - 1] = a[tmp];
            a[tmp] = change;
        }
        
        return b;
    }
    
    public static void main(String[] args) {
        int[] a = {1,2,3,4,5,6,7,8,9};
        System.out.println(Arrays.toString(srand(a)));
    }
}

3、 写出代码判断一个整数是不是2的阶次方(请代码实现,谢绝调用API方法)

import java.util.Scanner;

//判断整数是不是2的阶次方
public class Demo2 {
    
    public static boolean check(int sum) {
        boolean flag = true; //判断标志
        while(sum > 1) {
            if (sum % 2 == 0) {
                sum = sum/2;
            } else {
                flag = false;
                break;
            }
        }
        return flag;
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个整数:");
        int sum = scanner.nextInt();
        System.out.println(sum + " 是不是2的阶次方:" + check(sum));
    }
}

4、 假设今日是2015年3月1日,星期日,请算出13个月零6天后是星期几,距离现在多少天(请用代码实现,谢绝调用API方法)

import java.util.Scanner;

//算出星期几
public class Demo4 {
    public static String[] week = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
    public static int i = 0;
    public static int[] monthday1 = {0,31,28,31,30,31,30,31,31,30,31,30,31};
    public static int[] monthday2 = {0,31,29,31,30,31,30,31,31,30,31,30,31};
    
    //查看距离当前天数的差值
    public static String distance(int year,int month,int day,int newMonth,int newDay) {
        int sum = 0; //设定初始距离天数
        if (month + newMonth >= 12) {
            if (((year + 1) % 4 == 0 && (year + 1) % 100 != 0)||(year + 1) % 400 == 0) {
                sum += 366 + newDay;
                for(int i = 0;i < newMonth - 12;i++) {
                    sum += monthday1[month + i];
                }
            } else {
                sum += 365 + newDay;
                for(int i = 0;i < newMonth - 12;i++) {
                    sum += monthday1[month + i];
                }
            }
        } else {
            for(int i = 0;i < newMonth;i++) {
                sum += monthday1[month + i];
            }
            sum += newDay;
        }
        return week[sum%7];
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入当前年份");
        int year = scanner.nextInt();
        System.out.println("请输入当前月份");
        int month = scanner.nextInt();
        System.out.println("请输入当前天数");
        int day = scanner.nextInt();
        System.out.println("请输入当前是星期几:以数字表示,如:星期天 为 0");
        int index = scanner.nextInt();
        System.out.println("今天是:" + year + "-" + month + "-" + day + "  " + week[index]);
        
        System.err.println("请输入相隔月份");
        int newMonth = scanner.nextInt();
        System.out.println("请输入剩余天数");
        int newDay = scanner.nextInt();
        
        System.out.println("经过" + newMonth + "月" + newDay + "天后,是" + distance(year,month,day,newMonth,newDay));
    }
}

5、 有两个篮子,分别为A 和 B,篮子A里装有鸡蛋,篮子B里装有苹果,请用面向对象的思想实现两个篮子里的物品交换(请用代码实现)

//面向对象思想实现篮子物品交换
public class Demo5 {
    public static void main(String[] args) {
        //创建篮子
        Basket A = new Basket("A");
        Basket B = new Basket("B");
        
        //装载物品
        A.load("鸡蛋");
        B.load("苹果");
        
        //交换物品
        A.change(B);
        
        A.show();
        B.show();
    }
}

class Basket{
    public String name; //篮子名称
    private Goods goods; //篮子中所装物品
    
    public Basket(String name) {
        // TODO Auto-generated constructor stub
        this.name = name;
        System.out.println(name + "篮子被创建");
    }
    
    //装物品函数
    public void load(String name) {
        goods = new Goods(name);
        System.out.println(this.name + "装载了" + name + "物品");
    }
    
    public void change(Basket B) {
        System.out.println(this.name + " 和 " + B.name + "中的物品发生了交换");
        String tmp = this.goods.getName();
        this.goods.setName(B.goods.getName());
        B.goods.setName(tmp);
    }
    
    public void show() {
        System.out.println(this.name + "中有" + goods.getName() + "物品");
    }
}

class Goods{
    private String name; //物品名称
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Goods(String name) {
        // TODO Auto-generated constructor stub
        this.name = name;
    }
}

6、更多算法练习

更多算法练习题,请访问 leetcode-cn.com/problemset/…