《剑指offer》算法题第十天

今日题目:

  1. 数组中的逆序对
  2. 两个链表的第一个公共节点
  3. 数字在排序数组中出现的次数
  4. 二叉搜索树的第k大节点
  5. 字符流中第一个不重复的字符

 

1. 数组中的逆序对

题目描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。

思路:
这道题运用归并排序的思想来求解,归并排序是面试中经常问到的知识点,得经常翻出来熟悉熟悉。

代码如下:

 1 public class Solution {
 2     int cnt;
 3  
 4     public int InversePairs(int[] array) {
 5         cnt = 0;
 6         if (array != null)
 7             mergeSortUp2Down(array, 0, array.length - 1);
 8         return cnt;
 9     }
10  
11     /*
12      * 归并排序(从上往下)
13      */
14     public void mergeSortUp2Down(int[] a, int start, int end) {
15         if (start >= end)
16             return;
17         int mid = (start + end) >> 1;
18  
19         mergeSortUp2Down(a, start, mid);
20         mergeSortUp2Down(a, mid + 1, end);
21  
22         merge(a, start, mid, end);
23     }
24  
25     /*
26      * 将一个数组中的两个相邻有序区间合并成一个
27      */
28     public void merge(int[] a, int start, int mid, int end) {
29         int[] tmp = new int[end - start + 1];
30  
31         int i = start, j = mid + 1, k = 0;
32         while (i <= mid && j <= end) {
33             if (a[i] <= a[j])
34                 tmp[k++] = a[i++];
35             else {
36                 tmp[k++] = a[j++];
37                 cnt += mid - i + 1;  // core code, calculate InversePairs............
38                 cnt %= 1000000007;
39             }
40         }
41  
42         while (i <= mid)
43             tmp[k++] = a[i++];
44         while (j <= end)
45             tmp[k++] = a[j++];
46         for (k = 0; k < tmp.length; k++)
47             a[start + k] = tmp[k];
48     }
49 }

 

2.两个链表中的第一个公共节点

题目描述:
输入两个链表,找出它们的第一个公共结点。

思路:
《剑指offer》中的参考答案利用了两个栈来分别存储两个链表,然后从链尾到链头遍历两个链表,寻找公共的节点。但是博主觉得没必要那么麻烦,运用一个hashset就能够处理这道题。
另外,在这道题的讨论区中,有一个特别精妙的方法,利用两个指针遍历数组,第一趟得到链表长度的差值,第二趟寻找公共节点,下面也会贴出来。

代码如下:

//利用hashset
import java.util.HashSet;
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        if(pHead1 == null || pHead2 == null) return null;
        HashSet<ListNode> set = new HashSet();
        while(pHead1 != null){
            set.add(pHead1);
            pHead1 = pHead1.next;
        }
        while(pHead2 != null){
            if(set.contains(pHead2))
                return pHead2;
            pHead2 = pHead2.next;
        }
        return null;
    }
}


//两个指针的方法
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode p1 = pHead1;
        ListNode p2 = pHead2;
        while(p1 != p2){
            p1 = (p1==null ? pHead2 : p1.next);
            p2 = (p2==null ? pHead1 : p2.next);
        }
        return p1;
    }
}

 

 

3.数字在排序数组中出现的次数

题目描述:
统计一个数字在排序数组中出现的次数。

思路:
比较直接的方法是直接遍历数组,统计目标数字出现的次数,时间复杂度为O(n)。
比较快的方法是利用二分查找的思想,时间复杂度可达到O(logn)。

代码如下:

 1 public class Solution {
 2     int count = 0;
 3     public int GetNumberOfK(int [] array , int k) {
 4         binSearch(array,k,0,array.length-1);
 5         return count;
 6     }
 7     
 8     public void binSearch(int[] nums,int k,int start,int end){
 9         if(start == end){
10             if(nums[start] == k) count++;
11             return;
12         }else if(start < end){
13             int mid = (start+end)/2;
14             if(nums[mid] == k){
15                 count++;
16                 binSearch(nums,k,start,mid-1);
17                 binSearch(nums,k,mid+1,end);
18             }else if(nums[mid] < k){
19                 binSearch(nums,k,mid+1,end);
20             }else{
21                 binSearch(nums,k,start,mid-1);
22             }
23         }
24     }
25 }

 

 

4.二叉搜索树的第k大节点

题目描述:
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。

思路:
比较直接,二叉树的中序遍历。不过需要提一下的是,二叉树三种遍历顺序的递归和迭代实现方式都得非常熟悉。

代码如下:

 1 import java.util.Stack;
 2 public class Solution {
 3     TreeNode KthNode(TreeNode pRoot, int k){
 4         if(pRoot == null || k == 0) return null;
 5         Stack<TreeNode> stack = new Stack();
 6         int count = 0;
 7         while(pRoot != null || !stack.isEmpty()){
 8             if(pRoot != null){
 9                 stack.push(pRoot);
10                 pRoot = pRoot.left;
11             }else{
12                 pRoot = stack.pop();
13                 count++;
14                 if(count == k)
15                     return pRoot;
16                 pRoot = pRoot.right;
17             }
18         }
19         return null;
20     }
21 }

 

 

5.字符流中第一个不重复的字符

题目描述:
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

思路:
这道题和之前的求一个数据流中的中位数的题相似,重点是如何选择一个合适的数据结构来存储数据,是的插入和查询的效率都比较高。
在这道题中,《剑指offer》给的参考答案是声明一个int型的数组来存储各个字符的状态。这种思想非常地重要,应多做练习来熟悉。

代码如下:

 1 public class Solution {
 2     //Insert one char from stringstream
 3     int[] map = new int[256];
 4     int index = 1;
 5     public void Insert(char ch)
 6     {//0代表还未出现相应的字符,-1代表出现一次以上,其他数字代表什么时候出现
 7         if(map[ch] == 0)
 8             map[ch] = index;
 9         else if(map[ch] > 0)
10             map[ch] = -1;
11         index ++;
12     }
13   //return the first appearence once char in current stringstream
14     public char FirstAppearingOnce()
15     {
16         int min = Integer.MAX_VALUE;
17         int ind = -1;
18         for(int i = 0; i < 256; i++){//找出大于0的最小值及其坐标
19             if(map[i] > 0 && map[i] < min){
20                 min = map[i];
21                 ind = i;
22             }
23         }
24         if(ind == -1)//不存在大于0的数,则没有字符出现一次
25             return '#';
26         else
27             return (char)ind;
28     }
29 }

 

posted @ 2018-02-06 19:34  wezheng  阅读(154)  评论(0编辑  收藏  举报