Android面试指南(五)————算法篇

350 阅读5分钟

高频记录

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性复杂性
直接插入排序O(n2)O(n2)O(n2)O(n2)O(n)O(n)O(1)O(1)稳定简单
希尔排序O(nlog2n)O(nlog2n)O(n2)O(n2)O(n)O(n)O(1)O(1)不稳定较复杂
直接选择排序O(n2)O(n2)O(n2)O(n2)O(n2)O(n2)O(1)O(1)不稳定简单
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(1)O(1)不稳定较复杂
冒泡排序O(n2)O(n2)O(n2)O(n2)O(n)O(n)O(1)O(1)稳定简单
快速排序O(nlog2n)O(nlog2n)O(n2)O(n2)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)不稳定较复杂
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(nlog2n)O(n)O(n)稳定较复杂
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(d(n+r))O(d(n+r))O(d(n+r))O(n+r)O(n+r)稳定较复杂

冒泡排序(BubbleSort)

依次比较两个相邻的元素,如果顺序错误就交换,直到跑完所有元素,那么最后一个元素就是最大(最小)的值,在接着按照上述操作进行排序。

时间复杂度:O(n^2)

 private void bubbleSort(int a[]){
        int temp;
        for(int i = 0;i<a.length-1;i++){
            for(int j = 0;j<a.length-1-i;j++){
                if(a[j]>a[j+1]){
                    temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }
        }
    }

选择排序

第一趟找出最小的放在第一个,第二趟找出第二小的放在第二个,。。。。。

时间复杂度:O(n^2)

public static void selectSort(int a[]){
        for(int i = 0;i<a.length;i++){
            for(int j = i+1;j<a.length;j++){
                if(a[i]>a[j]){
                    int temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
        }
    }

快速排序(QuickSort)

分治法:将一大块分解成若干小块,一块一块算

  • 设置一边的值为哨兵
  • 将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  • 在对剩下的区间进行排序
private void quickSort(int a[],int left,int right){
    //结束条件
    if(left <right){
        //每一趟排序
        int base = division(a,left,right);
        //哨兵两边开始排序
        //左边
        quickSort(a,left,base-1);
        //右边
        quickSort(a,base+1,right);
    }
}
//使用分治法排序
private int division(int a[],int left,int right){
    //哨兵
    int base = a[left];
    while (left<right){
        //哨兵在左边,所以right左移
        while(left<right && a[right]>=base){
            right--;
        }
        if(left != right) {
            a[left] = a[right];
        }
        //哨兵在右边,所以left右移
        while(left<right && a[left]<=base){
            left++;
        }
        if(left != right) {
            a[right] = a[left];
        }
    }
    a[left] = base;
    return left;
}

二分插入:

 while (lo <= hi) {
            //二分法一分而二,数组中间下标
            final int mid = (lo + hi) >>> 1;
            //二分法一分而二,数组中间下标处的值
            final int midVal = array[mid];
            
            if (midVal < value) {
                /**
                如果数组中间处的值比要找的值小,代表要找的值
                在数组的中后部部分,所以当前下标取值为mid + 1
                */
                lo = mid + 1;
            } else if (midVal > value) {
                /**
                如果数组中间处的值比要找的值大,代表要找的值
                在数组的前中部部分,所以当前下标取值为mid - 1
                */
                hi = mid - 1;
            } else {
                //数组中间处的值与要找的值相等,直接返回数组中部的下标mid
                return mid;  // value found
            }
        }

扑克牌算法

根节点到目标节点的路径

找出最小的k个数

  1. 使用快速排序:O(nlogn)
       public static int[] getLeastNumbers1(int[] input, int k) {
        if (input == null || input.length < k) {
            return null;
        }
        int[] output = new int[k];
        pivotFindMinK(input, output, k, 0, input.length - 1);
        return output;
    }

    private static void pivotFindMinK(int[] numbers, int[] output, int k, int start, int end) {
        if (numbers == null || numbers.length == 0) {
            return;
        }
        int left = start;
        int right = end;
        int temp = numbers[left];
        while (left < right) {
            while (numbers[right] > temp && left < right) {
                right--;
            }
            numbers[left] = numbers[right];
            while (numbers[left] <= temp && left < right) {
                left++;
            }
            numbers[right] = numbers[left];
        }
        numbers[left] = temp;
        if (left > k-1) {
            pivotFindMinK(numbers, output, k, start, left - 1);
        } else if (left < k-1) {
            pivotFindMinK(numbers, output, k, right + 1, end);
        } else {
            System.arraycopy(numbers, 0, output, 0, k);
        }
    }
  1. 新建一个数组,放入数组前k个数,并由小到大排列好,遍历原数组,如果存在比新数组中的值更小,插入排序,而后得到新数组。
  2. 小根堆实现,按顺序放入小根堆---大顶堆(父节点<=子节点),小根堆大小为k,超过大小,删除堆顶元素,相反的话,加入大根堆---小顶堆(父节点>=子节点)

二分查找法

/**
	 * 使用递归的二分查找
	 *title:recursionBinarySearch
	 *@param arr 有序数组
	 *@param key 待查找关键字
	 *@return 找到的位置
	 */
	public static int recursionBinarySearch(int[] arr,int key,int low,int high){
		
		if(key < arr[low] || key > arr[high] || low > high){
			return -1;				
		}
		
		int middle = (low + high) / 2;			//初始中间位置
		if(arr[middle] > key){
			//比关键字大则关键字在左区域
			return recursionBinarySearch(arr, key, low, middle - 1);
		}else if(arr[middle] < key){
			//比关键字小则关键字在右区域
			return recursionBinarySearch(arr, key, middle + 1, high);
		}else {
			return middle;
		}	

一个整形数组里求两个数的和能不能等于一个给定数

  1. 如果数组是有序的,两个下标,如果相加大于给定数,左边--,如果小于给定数,右边++,直到数据相等或者右边等于左边
        int[] a = {1,4,5,7,9,12,56,456};
        int i = 0;
        int j = a.length-1;
        int sum = 65;
        while(i<j){
            if(a[i] + a[j] >sum){
                j--;
            }else if(a[i] + a[j] <sum){
                i++;
            }else{
                System.out.println(a[i] + "+" + a[j] + "=" + sum);
                break;
            }
        }
  1. 如果数组是无序的
public static void twoSum(int[] nums,int target){
        Map<Integer,Integer> map = new HashMap<>();
        for(int i = 0;i<nums.length;i++){
            System.out.println("--------");
            if(map.containsKey(nums[i])){
                System.out.println(nums[map.get(nums[i])] + "+" + nums[i] + "=" + target);
                break;
            }
            int value = target-nums[i];
            map.put(value,i);
        }
    }

连续子数组的最大和

    public int maxSubArray(int[] nums) {
        int max = nums[0];
        int sum = nums[0];
        for(int i = 1;i<nums.length;i++){
            if(nums[i] + sum < nums[i]){
                sum = nums[i];
            }else{
                sum +=nums[i];
            }
            if(sum > max){
                max = sum;
            }
        }
        return max;
    }

摩尔投票法

class Solution {
    public int majorityElement(int[] nums) {
        int x = 0, votes = 0;
        for(int num : nums){
            if(votes == 0) x = num;
            votes += num == x ? 1 : -1;
        }
        return x;
    }
}

二叉树

二叉树遍历

 //层序遍历
    public void levelOrder(BinaryTreeNode root) {
        BinaryTreeNode temp;
        Queue<BinaryTreeNode> queue = new LinkedList<BinaryTreeNode>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            temp = queue.poll();
            System.out.print(temp.getData() + "\t");
            if (null != temp.getLeft())
                queue.offer(temp.getLeft());
            if (null != temp.getRight()) {
                queue.offer(temp.getRight());
            }
        }
    }

 //前序遍历递归的方式
    public void preOrder(BinaryTreeNode root) {
        if (null != root) {
            System.out.print(root.getData() + "\t");
            preOrder(root.getLeft());
            preOrder(root.getRight());
        }
    }
  //前序遍历非递归的方式
    public void preOrderNonRecursive(BinaryTreeNode root) {
        Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
        stack.push(root);
        while (!stack.isEmpty()) {
            BinaryTreeNode node = stack.pop();
            System.out.print(node.getData() + "\t");
            if (node.getRight() != null) {
                stack.push(node.getRight());
            }
            if (node.getLeft() != null) {
                stack.push(node.getLeft());
            }
        }
    }
    //中序便利非递归
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.add(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            if(node.left != null){
                stack.add(node);
                stack.add(node.left);
            }else{
                list.add(node.val);
                if(node.right != null){
                    stack.add(node.right);
                }else{
                    while(!stack.isEmpty()){
                        TreeNode node1 = stack.pop();
                        list.add(node1.val);
                        if(node1.right != null){
                            stack.add(node1.right);
                            break;
                        }
                    }
                }
            }
        }
        return list;
    }
    
    //后序遍历
    public List<Integer> postorderTraversal(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        stack.add(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            list.add(node.val);
            if(node.left != null){
                stack.add(node.left);
            }
            if(node.right != null){
                stack.add(node.right);
            }
        }
        List<Integer> result = new ArrayList<>();
        for(int i = list.size()-1;i>=0;i--){
            result.add(list.get(i));
        }
        return result;
    }
		//之字形打印
		public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> listList = new ArrayList<>();
        if(root == null){
            return listList;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int i = 1;
        while(!queue.isEmpty()){
            LinkedList<Integer> list = new LinkedList<>();
            int count = queue.size();
            for(int j = 0;j<count;j++){
                TreeNode node = queue.poll();
                if(i%2 == 0){
                    list.addFirst(node.val);
                }else{
                    list.add(node.val);
                }
                if(node.left != null){
                    queue.add(node.left);
                }
                if(node.right != null){
                    queue.add(node.right);
                }
            }
            i++;
            if(list.size()>0){
                listList.add(list);
            }
        }
        return listList;
    }

两个view第一个公共父view

  1. 引入set集和,一个用来存贮a数据,将b放入a数组,当数组重复会返回false,放入另一个set中做返回。
public static Set<Integer> getIds(Integer[] a, Integer[] b){
	  
	  Set<Integer> same = new HashSet<Integer>();  //用来存放两个数组中相同的元素
	  Set<Integer> temp = new HashSet<Integer>();  //用来存放数组a中的元素
	  
	  for (int i = 0; i < a.length; i++) {
		  temp.add(a[i]);   //把数组a中的元素放到Set中,可以去除重复的元素
	  }
	  
	  for (int j = 0; j < b.length; j++) {
	    //把数组b中的元素添加到temp中
	    //如果temp中已存在相同的元素,则temp.add(b[j])返回false
		if(!temp.add(b[j]))
			same.add(b[j]);
	}
}

如果想知道两个数组的索引,使用map存储,key是值,value是数组索引,在进行map.containsKey()对比,找到则使用

  1. 将a数组放入set中,将b轮询放入set中,返回false,则重复值
public static void getIds(int a[], int b[]) {
        Set<Integer> set1 = new HashSet<>();
        for(Integer integer:a){
            set1.add(integer);
        }

        for(Integer j:b){
            if(!set1.add(j)){
                System.out.println(j);
                break;
            }
        }
    }
  1. 两个链表中第一个根节点

两个指针分别跑两条链,短的跑完后,将长的头赋值过去,再开始跑,长的跑完亦然,最终两指针相交地方为两链表的第一个根节点

 public static void getNode(Node nodeA, Node nodeB) {
        Node pA = nodeA;
        Node pB = nodeB;
        while (pA != pB) {
            if (pA.next == null) {
                pA = nodeB;
            } else {
                pA = pA.next;
            }
            if (pB.next == null) {
                pB = nodeA;
            } else {
                pB = pB.next;
            }
        }
        System.out.print(pA.value);
    }
  1. 二叉树的最近公共祖先
class Solution {
    TreeNode node;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        dps(root,p,q);
        return node;
    }

    public boolean dps(TreeNode root, TreeNode p, TreeNode q){
        if(root == null){
            return false;
        }
        if(root.val == p.val){
            node = p;
            return true;
        }
        if(root.val == q.val){
            node = q;
            return true;
        }
        boolean a = dps(root.left,p,q);
        boolean b = dps(root.right,p,q);
        if(a && b){
            node = root;
        }
        if(a || b){
            return true;
        }
        return false;
    }
}

判断链表是不是环链,并返回入口

两个指针,一个一次跑一个,一个一次跑两个,相交的话就是存在环链

将跑的慢的从头开始一个一个跑,跑的快的从相交的点开始跑,一个一个跑,最终相遇的就是入口

public static Node isCircle(Node node) {
        Node p = node;
        Node q = node;
        while (p != null && q != null) {
            p = p.next.next;
            q = q.next;
            if(p == q){
                while(node != p){
                  node = node.next;
                  p = p.next;
                }
              	return node;
            }
        }
       return p;
    }

有一个整型数组,包含正数和负数,将负数放在左边,且保证相对位置保持不变

转化成链表,遍历链表将复苏和单独成链后连接到剩下的正数链上

数组两个索引,一个索引记录负数的尾部,一个所以负责遍历,每找到一个负数,将其插入该值,后续值后移一位。

 public static void get(int a[]){
        for(int i = 0,j = 0;j<a.length;j++){
            if(a[j]<0){
                int temp = a[j];
                int k = j;
                while(i<=k-1){
                    a[k] = a[k-1];
                    k--;
                }
                a[i] = temp;
                i++;
            }
        }
        for(int i:a){
            System.out.println(i);
        }
    }

打印蛇形矩阵

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        if(matrix.length == 0) return new int[0];
        int l = 0, r = matrix[0].length - 1, t = 0, b = matrix.length - 1, x = 0;
        int[] res = new int[(r + 1) * (b + 1)];
        while(true) {
            for(int i = l; i <= r; i++) res[x++] = matrix[t][i]; // left to right.
            if(++t > b) break;
            for(int i = t; i <= b; i++) res[x++] = matrix[i][r]; // top to bottom.
            if(l > --r) break;
            for(int i = r; i >= l; i--) res[x++] = matrix[b][i]; // right to left.
            if(t > --b) break;
            for(int i = b; i >= t; i--) res[x++] = matrix[i][l]; // bottom to top.
            if(++l > r) break;
        }
        return res;
    }
}

求二叉树的叶子节点数

public int testTree(Tree tree){
        if(tree==null){
            return 0;
        }
        if(tree.left==null&&tree.right==null){
            System.out.println("叶子节点:"+tree.val);
            return 1;
        }
        return testTree(tree.left)+testTree(tree.right);
    }

求二叉树的深度

public static int treeDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 计算左子树的深度
        int left = treeDepth(root.left);
        // 计算右子树的深度
        int right = treeDepth(root.right);
        // 树root的深度=路径最长的子树深度 + 1
        return left >= right ? (left + 1) : (right + 1);
    }

动态规划

数组最大和

累加值:一个一个累加的值

最大值:记录累加值的最大值

假设数组为最大,每次添加一个值,如果i-1的值<0,那么把i放入累加值,如果>0,那么将i的值加到累加值,如果累加值>最大值,则更新最大值。

如果需要数组的最大和的下标,对最大值的下标进行记录

    public static void main(String[] args) {
        int[] array = {1,-2,3,10,-4,7,2,-5};
        Long begintime = System.nanoTime();
        int result =  FindGreatestSumOfSubArray(array);
        Long endtime = System.nanoTime();
        System.out.println("连续子数组的最大和为:"+result+",运行时间:"+(endtime - begintime) + "ns");

    }

    public static int FindGreatestSumOfSubArray(int[] array) {
        int len = array.length;
        if (len == 0){
            return 0;
        }
        int[] currentsum = new int[len];
        currentsum[0] = array[0];
        int greatsetsum = array[0];
        System.out.println("第1步:累加子数组和:"+currentsum[0]+",最大子数组和:"+greatsetsum);
        for(int i=1;i<array.length;i++){
            //下面是动态规划的状态转移方程
            if(currentsum[i-1]>0){
                currentsum[i] = currentsum[i-1] + array[i];
            }else{
                currentsum[i] = array[i];
            }
            //根据currentsum的值更新greatsetsum的值
            if(currentsum[i] > greatsetsum){
                greatsetsum  = currentsum[i];
            }
            System.out.println("第"+(i+1)+"步:累加子数组和:"+currentsum[i]+",最大子数组和:"+greatsetsum);
        }
        return greatsetsum;
    }

}