面试中高频出现的数据结构与算法题目

在面试中我们常常会碰到手写代码的情况,考验我们的临场反应和平时积累数据结构与算法的能力,在这里我整理一下在面试中那些高频出现的面试算法题,欢迎大家一起学习,同时这篇博客将会只有未完待续,没有完结,欢迎大家将自己的一些见解和学习心得评论交流~

首先这篇博客我先粗略的分为如下几类:

1.排序算法

2.二叉树问题

3.链表问题

4.字符串问题

目录

1.1 快速排序

1.2 归并排序

1.3 堆排序

1.4 冒泡排序

1.5 插入排序

1.6 选择排序

2.1 二叉树递归遍历

2.2 二叉树非递归遍历

2.3 “之”字型打印二叉树

3.1 反转单向链表

3.2 合并两个有序的单链表

4.1 最长无重复子串的长度

4.2 返回两个字符串的最长公共子串


1.1 快速排序

package com.ma.sort;
/**
 * 快速排序
 * 快速排序(英语:Quicksort),又稱劃分交換排序(partition-exchange sort),簡稱快排,一種排序算法,最早由
 * 東尼·霍爾提出。在平均狀況下,排序  n個項目要  O(n\log n)
 *  O(n\log n)大O符号)次比較。在最壞狀況下則需要 O(n^{2})
 * O(n^{2})次比較,但這種狀況並不常見。事實上,快速排序 Theta (n\log n)
 * Theta (n\log n)通常明顯比其他演算法更快,因為它的內部循环(inner loop)可以在大部分的架構上很有效率地達成。
 */
/**
 * 时间复杂度:O(nlog2n)
 */

/**
 * 空间复杂度:O(1)
 */

/**
 * 不稳定
 */

public class Quicksorting {
    public static void quickSort(int[] a, int low, int hi){
        if(low < hi){
            int middle = getMiddle(a, low, hi);
            quickSort(a, 0, middle - 1);
            quickSort(a, middle + 1, hi);
        }
    }
    public static int getMiddle(int[] a, int low, int hi){
        int key = a[low];
        while(low < hi){
            while(low <hi && a[hi] >= key){
                hi--;
            }
            a[low] = a[hi];
            while(low < hi && a[low] <= key){
                low++;
            }
            a[hi] = a[low];
        }
        a[low] = key;
        return low;
    }
    public static void main(String[] args) {
        int[] a = {3,2,5,8,4,7,6,9};
        quickSort(a, 0, a.length - 1);
        for (int i : a){
            System.out.print(i + " ");
        }
    }
}

1.2 归并排序

package com.ma.sort;
/**
 * 归并排序
 * 要将一个数组排序可以先将它递归的分成两部分进行排序,然后将结果归并起来。
 */
/**
 * 时间复杂度:O(nlog2n)
 */

/**
 * 空间复杂度:O(n)
 */

/**
 * 稳定
 */

public class Mergesort {
    private static int[] ca;//归并时候需要的辅助数组
    public static void sort(int[] a){
        ca = new int[a.length];
        sort(a,0,a.length - 1);
    }
    private static void sort(int[] a, int lo, int hi){
        //将数组a[lo...hi]排序
        if(hi <= lo){
            return;
        }
        int mid = lo + (hi - lo)/2;
        sort(a, lo, mid);//将左半边排序
        sort(a,mid + 1, hi);//将右半边排序
        merge(a, lo, mid, hi);
    }
    public static void merge(int[] a,int lo, int mid, int hi){
        //将a[lo...mid]和a[mid+1...hi]归并
        int i = lo,j = mid + 1;
        for(int k = lo; k <= hi; k++){
            ca[k] = a[k];
        }
        for(int k = lo; k <= hi; k++){
            if(i > mid) a[k] = ca[j++];
            if(j > hi) a[k] = ca[i++];
            else if(ca[j] < ca[i]) a[k] = ca[j++];
            else a[k] = ca[i++];
        }
    }

    public static void main(String[] args) {
        int[] a = {3,2,5,8,4,7,6,9};
        sort(a);
        for(int i : a){
            System.out.print(i + " ");
        }
    }
}

1.3 堆排序

package com.ma.sort;
/**
 * 堆排序
 * 堆排序分为两个阶段。在堆的构建过程中我们将原始数组重新组织安排进一个堆中;然后在下沉排序阶段,我们从堆中按照
 * 递减顺序取出所有元素并得到排序结果。
 */
/**
 * 时间复杂度:O(nlog2n)
 */

/**
 * 空间复杂度:O(1)
 */

/**
 * 不稳定
 */

public class Heapsorting {
    /*
    创建最小堆
     */
    public static void createLittleHeap(int[] a, int last){
        for(int i = (last - 1) / 2; i >= 0; i--){//找到最后一个叶子节点的双亲节点
            //保存当前正在判断的节点
            int parent = i;
            //若当前节点的左节点存在,即子节点存在
            while(2 * parent + 1 <= last){
                //bigger指向左节点
                int bigger = 2 * parent + 1;
                //说明存在右节点
                if(bigger < last){
                    //右子节点大于左子节点时
                    if(a[bigger + 1] > a[bigger]){
                        bigger = bigger + 1;
                    }
                }
                if(a[parent] <= a[bigger]){
                    swap(a, parent, bigger);
                    parent = bigger;
                }else{
                    break;
                }
            }
        }
    }
    public static  void swap(int[] a, int i, int j){
        a[i] = a[i] + a[j];
        a[j] = a[i] - a[j];
        a[i] = a[i] - a[j];
    }
    public static void main(String[] args) {
        int[] a = {3,2,5,8,4,7,6,9};
        for(int i = 0; i < a.length; i++){
            createLittleHeap(a, a.length - 1 - i);
            swap(a, 0, a.length -1 -i);
        }
        for(int i : a){
            System.out.print(i + " ");
        }
    }
}

1.4 冒泡排序

package com.ma.sort;

import com.sun.org.apache.bcel.internal.generic.SWAP;

/**
 * 冒泡排序
 */
/**
 * 时间复杂度:O(n²)
 */

/**
 * 空间复杂度:O(1)
 */

/**
 * 不稳定
 */
public class Bubble {
    public static void sort(int[] array){
        for(int i = 0; i < array.length - 1; i++){
            for(int j = 0; j < array.length - i-1; j++){
                if(array[j] > array[j + 1]){
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j +1] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {3,2,5,8,4,7,6,9};
        sort(array);
        for(int a : array){
            System.out.print(a+" ");
        }
    }
}

1.5 插入排序

package com.ma.sort;
/**
 * 插入排序
 * 通常人们整理桥牌的方法是一张一张来,将每一张排插入到其余已有牌的适当位置。
 * 在计算机的实现中,为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位,这种算法
 * 叫做插入排序。
 */
/**
 * 时间复杂度:O(n²)
 */

/**
 * 空间复杂度:O(1)
 */

/**
 * 稳定
 */
public class Insertionsort {
    public static void sort(int[] a){
        int N = a.length;
        for(int i = 1; i < N; i++){
            for(int j = i; j > 0 && a[j] < a[j -1]; j--){
                int tmp = a[j];
                a[j] = a[j - 1];
                a[j - 1] = tmp;
            }
        }
    }

    public static void main(String[] args) {
        int[] a = {3,2,5,8,4,7,6,9};
        sort(a);
        for(int i : a){
            System.out.print(i + " ");
        }
    }
}

1.6 选择排序

package com.ma.sort;

/**
 * 选择排序
 * 一种最简单的排序方法,首先,找到数组中最小的元素,其次,将它和数组第一个元素交换位置。
 * 再次,在剩下的元素中找到最小的元素和第二个位置的元素进行交换。
 */

/**
 * 时间复杂度:O(n²)
 */

/**
 * 空间复杂度:O(1)
 */

/**
 * 不稳定
 */
public class Selectionsorting {
    public static void sort(int[] a){
        for(int i = 0; i < a.length; i++){
            for(int j = i +1; j < a.length; j++){
                if(a[j] < a[i]){
                    int tmp = a[i];
                    a[i] = a[j];
                    a[j] = tmp;
                }
            }
        }
    }
    public static void main(String[] args) {
        int[] a = {3,2,5,8,4,7,6,9};
        sort(a);
        for (int i : a){
            System.out.print(i+" ");
        }
    }
}

2.1 二叉树递归遍历

/**
       * 二叉树先序递归遍历
       */
      public class Node{
            public int value;
            public Node left;
            public Node right;
            
            public Node(int data){
                  this.value = data;
            }
      }
      public void preOrderRecur(Node head){
            if(head == null){
                  return ;
            }
            System.out.println(head.value+"");
            preOrderRecur(head.left);
            preOrderRecur(head.right);
      }

/**
       * 二叉树中序递归遍历
       */
      public class Node{
            public int value;
            public Node left;
            public Node right;
            
            public Node(int data){
                  this.value = data;
            }
      }
      public void inOrderRecur(Node head){
            if(head == null){
                  return ;
            }
            inOrderRecur(head.left);
            System.out.println(head.value+"");
            inOrderRecur(head.right);
      }
/**
       * 二叉树后序递归遍历
       */
      public class Node{
            public int value;
            public Node left;
            public Node right;
            
            public Node(int data){
                  this.value = data;
            }
      }
      public void posOrderRecur(Node head){
            if(head == null){
                  return ;
            }
            posOrderRecur(head.left);
            posOrderRecur(head.right);
            System.out.println(head.value+"");
      }

2.2 二叉树非递归遍历

/**
       * 非递归先序遍历
       */
      public void preOrderUnRecur(Node head){
            if(head!=null){
                  Stack<Node> stack = new Stack<Node>();
                  stack.add(head);
                  while(!stack.isEmpty()){
                        head = stack.pop();
                        System.out.println(head.value+"");
                        if(head.right!=null){
                              stack.push(head.right);
                        }
                        if(head.left!=null){
                              stack.push(head.left);
                        }
                  }
            }
            System.out.println("");
      }
/**
       * 非递归中序遍历
       */
      public void preOrderUnRecur(Node head){
            if(head!=null){
                  Stack<Node> stack = new Stack<Node>();
                  while(!stack.isEmpty()||head!=null){
                        if(head!=null){
                              stack.push(head);
                              head=head.left;
                        }else{
                              head=stack.pop();
                              System.out.println(head.value+"");
                              head=head.right;
                        }
                  }
            }
            System.out.println("");
      }
/**
       * 非递归后序遍历
       */
      public void preOrderUnRecur(Node head){
            if(head!=null){
                  Stack<Node> s1 = new Stack<Node>();
                  Stack<Node> s2 = new Stack<Node>();
                  s1.push(head);
                  while(!s1.isEmpty()){
                        head=s1.pop();
                        s2.push(head);
                        if(head.left!=null){
                              s1.push(head.left);
                        }
                        if(head.right!=null){
                              s1.push(head.right);
                        }
                  }
                  while(!s2.isEmpty()){
                        System.out.println(s2.pop().value+"");
                  }
            }
            System.out.println("");
      }

2.3 “之”字型打印二叉树

package com.ma.twoforkedtree;

import java.util.LinkedList;
import java.util.Stack;

public class ZhiPrint {
    public static void print(TreeNode root){
        if(root == null){
            return;
        }
        Stack<TreeNode> s1 = new Stack<>();//存放奇数行
        Stack<TreeNode> s2 = new Stack<>();//存放偶数行
        s1.push(root);
        while(! s1.isEmpty()){
            //弹出奇数行的元素
            TreeNode node = s1.pop();
            System.out.print(node.value + " ");
            //判断该行是否结束
            if(s1.isEmpty()){
                System.out.print(" ");
            }
            //给s2中先左后右压入节点
            if(node.left != null){
                s2.push(node.left);
            }
            if(node.right != null){
                s2.push(node.right);
            }
        }

        while(! s2.isEmpty()){
            //弹出偶数行的元素
            TreeNode node = s2.pop();
            System.out.print(node.value + " ");
            //判断该行是否结束
            if(s2.isEmpty()){
                System.out.print(" ");
            }
            //给s1中先右后左压入节点
            if(node.right != null){
                s1.push(node.right);
            }
            if(node.left != null){
                s1.push(node.left);
            }
        }

    }
    public static void main(String[] args) {
        //用队列构建一个完全二叉树
        LinkedList<TreeNode> q = new LinkedList<>();
        int i = 1;
        TreeNode root = new  TreeNode(i++);
        q.add(root);
        while(!q.isEmpty() && i < 16){
            TreeNode lchild = new TreeNode(i++);
            TreeNode rchild = new TreeNode(i++);
            TreeNode node = q.poll();
            node.left = lchild;
            node.right = rchild;
            q.add(node.left);
            q.add(node.right);
        }
        print(root);
    }
}
class TreeNode{
    int value;
    TreeNode left;
    TreeNode right;
    public TreeNode(int value){
        this.value = value;
    }
}

3.1 反转单向链表

public class Node{
            public int value;
            public Node next;
            public Node(int data){
                  this.value = data;
            }
      }
      
      public Node reverseList(Node head){
            Node pre = null;
            Node next = null;
            while(head != null){
                  next  = head.next;
                  head.next = pre;
                  pre = head;
                  head = next;
            }
            return pre;
      }

3.2 合并两个有序的单链表

public class Node{
            public int value;
            public Node next;
            
            public Node(int data){
                  this.value = data;
            }
      }
      public Node merge(Node head1, Node head2){
            if(head1 == null || head2 == null){
                  return head1 != null ? head1 : head2;
            }
            Node head = head1.value < head2.value ? head1 : head2;
            Node cur1 = head == head1 ? head1 : head2;
            Node cur2 = head == head1 ? head2 : head1;
            Node pre = null;
            Node next = null;
            while(cur1 != null && cur2 != null){
                  if(cur1.value <= cur2.value){
                        pre = cur1;
                        cur1 = cur1.next;
                  }else{
                        next = cur2.next;
                        pre.next = cur2;
                        cur2.next = cur1;
                        pre =cur2;
                        cur2 = next;
                  }
            }
            pre.next = cur1 == null ? cur2 : cur1;
            return head;
      }

4.1 最长无重复子串的长度

package com.ma.characterString;

/**
 *  题目:
 *  给定一个字符串 str,返回 str 最长无重复子串的长度。
 *  举例:
 *  str = "abcd" 返回 4
 *  str = "aabcb",最长无重复子串为"abc",返回3。
 */

/**
 * 解答:
 * 如果 str 长度为 N,字符编码范围是M,本题可以做到时间复杂度为O(N),额外空间复杂度为O(M)。
 *
 * 1.在遍历str之前,先申请几个变量。哈希表 map,key表示某个字符,value为这个字符第一次出现的位置。
 * 整型变量pre,如果当前遍历到字符str[i],pre表示必须以str[i - 1]字符结尾的情况下,最长无重复子串
 * 开始位置的前一个位置,初始时pre = -1。整型变量len,记录每一个字符结尾的情况下,最长无重复子串
 * 长度的最大值,初始时,len = 0。从左到右遍历字符串str,假设现在遍历到str[i],接下来求必须以str[i]
 * 结尾的情况下,最长无重复子串的长度。
 *
 * 2.map(str[i])的值表示之前遍历中最近一次出现str[i]的位置,假设在a位置。想要求以str[i]结尾的最长无
 * 重复子串,a位置必然不能包含进来,因为str[a]等于str[i]。
 *
 * 3.根据pre的定义,pre + 1 表示必须以str[i -  1]字符结尾的情况下,最长无重复子串开始的位置,假设在
 * a位置,也就是说,以str[i -1 ]结尾的最长无重复子串是向左扩展到pre位置才停止的。
 *
 * 4.如果pre的位置在a的左边,因为str[a]的位置是不能包含进来的,而str[a+1...i-1]上是不重复的,所以
 * 以str[i]结尾的最长无重复子串就是str[a+1...i]。如果pre的位置在a的右边,以str[i]结尾的最长无重复子串
 * 是向左扩充到pre结束的。所以以str[i]结尾的最长无重复子串扩充到pre也会停止,而且str[pre+1... i-1]
 *上肯定不含有str[i],s所以以str[i]结尾的最长无重复子串就是str[pre+1...i]
 *
 * 5.计算完长度后,pre位置和a位置哪一个在右边哪一个就作为新的pre值。然后计算下一个pre的值。所有长度
 * 的最大值用len记录。
 */
public class Longestnonrepeatingsubstring {
    public static int maxUnique(String str){
        if(str == null || str == ""){
            return 0;
        }
        char[] chas = str.toCharArray();
        int[] map = new int[256];
        for(int i = 0; i < 256; i++){
            map[i] = -1;
        }
        int pre = -1;
        int cur = 0;
        int len = 0;
        for(int i = 0; i != chas.length; i++ ){
            pre = Math.max(pre, map[chas[i]]);
            cur = i - pre;
            len = Math.max(len, cur);
            map[chas[i]] = i;
        }
        return len;
    }

    public static void main(String[] args) {
        String str = new String("abcabcdebc");
        System.out.println(maxUnique(str));

    }
}

4.2 返回两个字符串的最长公共子串

package com.ma.characterString;

/**
 * 题目:
 * 给定两个字符串 str1 和 str2,返回两个字符串的最长公共子串。
 *举例:
 * str1 = "1AB2345CD",str2 = "12345EF",返回"2345"。
 */

/**
 * 解答:
 * 经典动态规划的方法可以做到时间复杂度为O(M*N),额外空间复杂度为O(M*N)。
 * 首先需要生成动态规划表。生成大小为M*N的矩阵dp,行数为M,列数为N。dp[i][j]的含义是:
 * 在必须把str1[i]和str2[j]当作公共子串最后一个字符的情况下,最长公共子串能有多长。
 * 比如,str1 = "A1234B",str2 = "CD1234",dp[3][4]的含义是在必须把str1[3]和str2[4]当作公共子串
 * 最长能有多长。这时候最长公共子串为"123"所以dp[3][4]为3。
 * 再如,str1 = "A12E4B",str2 = "CD12F4",dp[3][4]的含义是必须把str1[3]和str2[4]当作公共子串的最后一个
 * 字符的情况下,公共子串能有多长。显然这种情况不可能构成公共子串。dp[3][4]为0。
 */

/**
 * 1.矩阵dp第一列即dp[0..M-1][0]。对某一个位置(i,0)来说,如果str[i] == str2[0],令dp[i][0] = 1,
 * 否则令dp[i][0] = 0。
 * 比如str1 = "ABAC" str2 = "A"。dp矩阵第一列上的值依次为dp[0][0] = 1,dp[2][0] = 0,dp[3][0] = 1,
 * dp[3][0] = 0。
 * 2.矩阵dp第一行dp[0][0...N-1]与步骤1同理。对某一个位置(0,j)来说,如果str1[0] == str2[j],令
 * dp[0][j] = 1,否则令dp[0][j] = 0。
 * 3.其他位置按照从左到右,再从上到下来计算,dp[i][j]的值只能有两种情况。
 * 3.1如果str1[i] != str2[j]说明在必须把str1[i]和str2[j]作为公共子串的最后一个字符是不可能的,令
 * dp[i][j] = 0。
 * 3.2如果str1[i] == str[j]说明str1[i]和str2[j]可以作为公共子串最后一个字符,从最后一个字符能向
 * 左扩多大的长度呢?就是dp[i-1][j-1]的值,所以令dp[i][j] = dp[i-1][j-1]+1。
 */

/**
 * 生成动态规划表之后,得到最长公共子串是非常容易的。
 */
public class LongestPublicCharacterString {
    public static int[][] getdp(char[] str1, char[] str2){
        int[][] dp = new int[str1.length][str2.length];
        for(int i = 0; i < str1.length; i++){
            if(str1[i] == str2[0]){
                dp[i][0] = 1;
            }
        }
        for(int j = 0; j < str2.length; j++){
            if(str2[j] == str1[0]){
                dp[0][j] = 1;
            }
        }
        for(int i = 1; i < str1.length; i++){
            for(int j = 1; j < str2.length; j++){
                if(str1[i] == str2[j]){
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
            }
        }
        return dp;
    }

    public  static String lcst1(String str1, String str2){
        if(str1 == null || str2 == null || str1.equals("") || str2.equals("")){
            return "";
        }
        char[] chs1 = str1.toCharArray();
        char[] chs2 = str2.toCharArray();
        int[][] dp = getdp(chs1,chs2);
        int end = 0;
        int max = 0;
        for(int i = 0; i < chs1.length; i++){
            for(int j = 0; j < chs2.length; j++){
                if(dp[i][j] > max){
                    end = i;
                    max = dp[i][j];
                }
            }
        }
        return str1.substring(end - max + 1, end  + 1);
    }

    public static void main(String[] args) {
        String str1 = "abcbcbbc";
        String str2 = "cbcbaaa";
        System.out.println(lcst1(str1, str2));
    }
}

猜你喜欢

转载自blog.csdn.net/young_1004/article/details/82962277
今日推荐