劍指offer(34-40題)詳解

歡迎關注個人數據結構專欄
劍指offer系列
劍指offer(1-10題)詳解
劍指offer(11-25題)詳解
劍指offer(26-33題)詳解
劍指offer(34-40題)詳解
劍指offer(51-59題)詳解
劍指offer(34-40題)詳解
微信公衆號:bigsai
在這裏插入圖片描述
聲明:大部分題基本未參考題解,基本爲個人想法,如果由效率太低的或者錯誤還請指正!,如果有誤導,還請指正!

34 第一個只出現一次的字符

題目描述

在一個字符串(0<=字符串長度<=10000,全部由字母組成)中找到第一個只出現一次的字符,並返回它的位置, 如果沒有則返回 -1(需要區分大小寫)

思路
用hashmap儲存記錄每個字母出現的次數。然後再進行遍歷查找第一個出現一次的字母返回位置。

實現代碼:

import java.util.HashMap;
public class Solution {
  public  int FirstNotRepeatingChar(String str) {
		HashMap<String, Integer>map=new HashMap<String, Integer>();
		String team="";
		for(int i=0;i<str.length();i++)
		{
			team=str.charAt(i)+"";		
			if(map.containsKey(team))
			{
				map.put(team, 2);
			}
			else {
				map.put(team, 1);
			}
		}
		int index=-1;
		for(int i=0;i<str.length();i++)
		{
			team=str.charAt(i)+"";
			if(map.get(team)==1)
			{
				index=i;
				break;
			}
		}
        return index;
    }
}

35 數組中的逆序數

題目描述

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007
輸入描述:
題目保證輸入的數組中沒有的相同的數字

數據範圍

對於%50的數據,size<=10^4

對於%75的數據,size<=10^5

對於%100的數據,size<=2*10^5

示例1

輸入
1,2,3,4,5,6,7,0
輸出
7

思路
至於逆序數,一般有三種方法求解,首當其中的就是暴力的O(n2)的方法,但是這種方法複雜度過高一看這個數據範圍肯定是過不了的。18年夏天遇到過逆序數記錄下來,但是寫的不夠好,從寫了一下!

今天剛寫歸併排序和逆序數各位一定好好看看,進行了詳細分析。

然後可以採用樹狀數組或者歸併排序求解逆序數。當然,筆者這裏採用的是歸併排序。

實現代碼爲:

public class Solution {
    static int value=0;
  public static int InversePairs(int [] array) {		
		mergesort(array,0,array.length-1);
		return value;
	
	}	
	private static void mergesort(int[] array, int l, int r) {
		int mid=(l+r)/2;
		if(l<r)
		{
			mergesort(array, l, mid);
			mergesort(array, mid+1, r);
			merge(array, l,mid, r);
		}
	}

	private static void merge(int[] array, int l, int mid, int r) {
		
		int lindex=l;int rindex=mid+1;
		int team[]=new int[r-l+1];
		int teamindex=0;
		while (lindex<=mid&&rindex<=r) {
			if(array[lindex]<=array[rindex])
			{
				team[teamindex++]=array[lindex++];
			}
			else {				
				team[teamindex++]=array[rindex++];
				value+=mid-lindex+1;
                value%=1000000007;
			}
		}
		while(lindex<=mid)
	      {
	    	  team[teamindex++]=array[lindex++];
	    	  
	      }
		while(rindex<=r)
	      {
	    	  team[teamindex++]=array[rindex++];
	      }	
		for(int i=0;i<teamindex;i++)
		{
			array[l+i]=team[i];
		}	
	}
}

36 兩個鏈表的第一個公共節點

題目描述

輸入兩個鏈表,找出它們的第一個公共結點

思路
這題的吧不要用暴力匹配的O(n2)的方法。大家直到鏈表是一條直的對吧?然後一個鏈表節點相同也就是後面都相同是吧? 所以如果有相同節點的話那麼一定從那個節點到最後都是相同的。並且肯定在短的後面纔可能匹配。

至於實現方式其實還是蠻多的,比如你可以先遍歷一次分別把兩個長度求出來然後把長的節點往後移到相同長度,然後一個個比較就好。當然這樣可能跑2次但是影響確實不大。你也可以藉助外部空間然後將遍歷一次的存下來。然後從後往前找到第一個不一樣的那個後面就是了。 當然筆者採用上面一個方法並沒有採用外部空間。

在這裏插入圖片描述
實現代碼爲:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
   public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
	          int lp1=getlen(pHead1);	
	          int lp2=getlen(pHead2);
	          while (lp1>lp2) {
				pHead1=pHead1.next;lp1--;
			}
	          while (lp1<lp2) {
					pHead2=pHead2.next;lp2--;
				}
	          while (pHead1!=pHead2) {
				pHead1=pHead1.next;
				pHead2=pHead2.next;
			}
	          return pHead1;
	    }
	private int getlen(ListNode pHead1) {
		int len=0;
		ListNode team=pHead1;
		while (team!=null) {
		     team=team.next;
		     len++;
		}
		return len;
	}
}

37 數字在排序數組中出現的次數

題目描述

統計一個數字在排序數組中出現的次數

思路
數組問題,一般枚舉能過但是不夠優雅。有序的數組、序列當然可以使用二分法哈!先二分找到其中任意一個數字所在的序號,然後根據這個座標左右試探就可以直到多少個啦!

實現代碼爲:

public class Solution {
     public int GetNumberOfK(int [] array , int k) {
	       int l=0,r=array.length;
	       int mid=(l+r)/2;
	       while (l<r) {
			if(array[mid]==k)
			{
				return search(array,mid);
			}
			else if (array[mid]<k) {
				l=mid+1;mid=(l+r)/2;
			}
			else {
				r=mid;mid=(l+r)/2;
			}
		}
	       return 0;
	    }

	private int search(int[] array, int mid) {
		int l=mid-1,r=mid+1;
		int value=0;
		while (l>=0&&array[l]==array[mid]) {
			value++;l--;
		}
		while (r<array.length&&array[r]==array[mid]) {
			value++;r++;
		}
		return value+1;
	}
}

38 二叉樹的深度

題目描述

輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度爲樹的深度

思路
因爲二叉樹有 左右子節點,左右節點的問題。每個孩子都要比自己更深一層,對於這個問題,你可以自己定義一個包含次數的結構體或者類用隊列或者棧進行遍歷找到最大的。但是這題可以直接使用遞歸進行求解。父節點和子節點之間的關係就是deep(parent)=max(parent.left,parent.right)+1。當然,這裏的left和right節點也要判空之類的處理。

實現代碼爲:

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
		if(root==null)return 0;
		 return Math.max(TreeDepth(root.left), TreeDepth(root.right))+1;	        
	    }
}

39 平衡二叉樹

題目描述

輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

思路
至於平衡二叉樹(avl),之所以平衡,是因爲它的任意一個左右節點的高度相差都小於等於1. 如果有不瞭解的可以參考以前寫的一篇avl二叉平衡樹 。但是這個跟文中寫的的其實不太一樣,是因爲之前我創造二叉樹的時候有個height值每次插入的時候動態維護,判斷是否平衡。但是顯然這題什麼都沒有,需要自己完成所有

好吧,來就來,不就是判斷每個節點都要滿足左右高度都要相差在1之內嘛!上面那題,不就是求高度的嘛?!!我用個隊列把所有點存下來,然後一個個判斷可還行?雖然可能效率不一定高。但是還是能過的。

實現代碼爲:

import java.util.ArrayDeque;
import java.util.Queue;
public class Solution {
     public boolean IsBalanced_Solution(TreeNode root) {
		 if(root==null)return true;
	        Queue<TreeNode>q1=new ArrayDeque<TreeNode>();
	        q1.add(root);
	        while (!q1.isEmpty()) {
	        	TreeNode team=q1.poll();
				if(Math.abs(TreeDepth(team.left)-TreeDepth(team.right))<=1)
				{
					if(team.left!=null)q1.add(team.left);
					if(team.right!=null)q1.add(team.right);
				}
				else {
					return false;
				}		
			}
	        return true;
	    }

	 public int TreeDepth(TreeNode root) {
			if(root==null)return 0;
			 return Math.max(TreeDepth(root.left), TreeDepth(root.right))+1;        
		    }
}

40 數組中只出現一次的數字

題目描述

一個整型數組裏除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

思路
這題只能用普通方法過,但是還是有技巧的。感覺可以用位運算但是奈何自己沒想出來。兩個相同的數進行異或^運算等於0.而0和任何數異或等於它自己。如果只求1個數那可能好搞一些,但是後面會研究討論區大佬的位運算方案

對於普通思路,可能大部分人會用hashmap先添加,最後遍歷兩個次數等於1個的兩個數返回。但是其實可以使用hash進行減。如果存在這個元素,就減去這個元素。如果不存在那就加入hash中,那麼最終剩的2個元素其實就是我們出現一次的元素。由於java底層用hashmap維護hashset我就用hashmap了差不了太大。

實現代碼爲:


import java.util.HashMap;
//num1,num2分別爲長度爲1的數組。傳出參數
//將num1[0],num2[0]設置爲返回結果
public class Solution {
   public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
	        HashMap<Integer, Integer>map=new HashMap<Integer, Integer>();
	        for(int i=0;i<array.length;i++)
	        {
	        	if(map.containsKey(array[i])) {map.remove(array[i]);}
	        	else {
					map.put(array[i],1);
				}
	        }
	        int count=0;
	        for(Integer va:map.keySet())
	        {
	        	if(count++==0)num1[0]=va;
	        	else {
					num2[0]=va;
				}
	        }
	    }
}

參考討論區:
這題的討論區很妙,用^來解決問題。至於討論區的題解稍微解釋一下:大概就是將數組一分爲2份
就是說先異或到最後得到一個數,肯定是a^b的值。這個數位位1的那個肯定就是兩個位不同的。比如10100.第三位就是a和b不同的,然後我們就再次遍歷所有數,用兩個數進行異或,如果第三位爲1xx那麼和a1異或,如果爲0xx那麼和a2異或。就相當於把這個數邏輯一分爲2.因爲所有數除了這兩都出現兩次,所以是可以消下去的!
在這裏插入圖片描述
實現代碼爲:

鏈接:https://www.nowcoder.com/questionTerminal/e02fdb54d7524710a7d664d082bb7811?f=discussion
來源:牛客網

public class Solution {
    public void FindNumsAppearOnce(int[] array, int[] num1, int[] num2)    {
        int length = array.length;
        if(length == 2){
            num1[0] = array[0];
            num2[0] = array[1];
            return;
        }
        int bitResult = 0;
        for(int i = 0; i < length; ++i){
            bitResult ^= array[i];
        }
        int index = findFirst1(bitResult);
        for(int i = 0; i < length; ++i){
            if(isBit1(array[i], index)){
                num1[0] ^= array[i];
            }else{
                num2[0] ^= array[i];
            }
        }
    }
     
    private int findFirst1(int bitResult){
        int index = 0;
        while(((bitResult & 1) == 0) && index < 32){
            bitResult >>= 1;
            index++;
        }
        return index;
    }
     
    private boolean isBit1(int target, int index){
        return ((target >> index) & 1) == 1;
    }
}

歡迎關注公衆號:bigsai 長期奮戰輸出!

發佈了196 篇原創文章 · 獲贊 1879 · 訪問量 75萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章