劍指offer(60-67題)詳解

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

到這裏,劍指offer就搞完了2020-01-192020-02-29(大年初五)和100人一起,第一次組織吧,特記下!歡迎關注公衆號:bigsai下次再一起玩!

60 把二叉樹打印成多行

題目描述

從上到下按層打印二叉樹,同一層結點從左至右輸出。每一層輸出一行。

思路
這題和上面59思路差不多,甚至更容易一些,對於層序遍歷。需要分層,所以用2個隊列進行區別一下層就可以了。

實現代碼爲:

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;

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

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
  ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {		    
		 ArrayList<ArrayList<Integer>>list=new ArrayList<ArrayList<Integer>>();
			if(pRoot==null)return list;
			Queue<TreeNode>q1=new ArrayDeque<TreeNode>();
			Queue<TreeNode>q2=new ArrayDeque<TreeNode>();
			q1.add(pRoot);
			while (!q1.isEmpty()||!q2.isEmpty()) {
				ArrayList<Integer>list2=new ArrayList<Integer>();
				while (!q1.isEmpty()) {
					TreeNode team=q1.poll();
					list2.add(team.val);
					if(team.left!=null)q2.add(team.left);
					if(team.right!=null)q2.add(team.right);					
				}
				list.add(list2);
				q1.addAll(q2);q2.clear();//把q2全部加進q1。然後clear掉
			}
			return list;			
	    }  
}

61 序列化二叉樹

題目描述

請實現兩個函數,分別用來序列化和反序列化二叉樹

二叉樹的序列化是指:把一棵二叉樹按照某種遍歷方式的結果以某種格式保存爲字符串,從而使得內存中建立起來的二叉樹可以持久保存。序列化可以基於先序、中序、後序、層序的二叉樹遍歷方式來進行修改,序列化的結果是一個字符串,序列化時通過 某種符號表示空節點(#),以 ! 表示一個結點值的結束(value!)。

二叉樹的反序列化是指:根據某種遍歷順序得到的序列化字符串結果str,重構二叉樹。

思路
對於序列化的概念,大家肯定會想起面向對象啦,json之類的開發相關,序列化反序列化就是能夠將一種結構變成字符存儲,而反序列化就是能夠根據制定的某種規則構造出原結構相同的對象

那麼這題個人有兩個想法吧,但是實現只實現了一個。

思路一
二叉樹是可以用數組存儲的,雖然用數組遇到不規則的二叉樹空間利用效率會很低,但是是可以存的。對於第n個節點。它的兩個兒子是2n2n+1(對應數組的物理地址)。

  • 所以實現上序列化你建立一個TreeNode[10000]數組,然後從根節點向後遍歷,如果有兒子放到對應位置。(當然這個實現上有點盲目性,不太好)。返回字符串如果有值返回對應值沒值的話可以用個特殊字串佔位置。
  • 而反序列化你只需要把字符串先放到對應數組中,然後建立TreeNode[10000]數組與上面的遍歷關聯,如果有值那麼就在這個位置建立TreeNode節點。再次遍歷的時候如果第n個節點的left指向2n,right指向2n+1位置.最終返回第1個位置的節點即可!反序列化完成。

在這裏插入圖片描述

思路二
我們知道:一箇中序帶着前序或者後序可以確定一個二叉樹!好了,我們就用前序可中序完成。前面講過二叉樹的幾種遍歷。在非遞歸遍歷的前序和中序可以共用一個過程!詳細可以參考數據結構專欄哈!

  • 序列化:給你個根節點,非遞歸前序和中序序列可以搞出來吧?但是記住字符串要有個東西分割,不能直接加。比如19 8中間必須有個東西分隔開來,這個要注意。最後直接將兩個字符相加返回即可。
  • 反序列化: 給你帶有前序和中序的字符串。分割出前序和中序序列。前面第四題是構建二叉樹就是用前序和中序構建二叉樹的,就是遞歸的妙用,至於還原具體的思路還請還對應劍指offer的題解

實現代碼爲:

import java.util.Stack;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
           static  String Serialize(TreeNode root) {
		  if(root==null)return "";
	    	String qian="";
	    	String zhong="";
	    	
	    	Stack<TreeNode>stack=new Stack<TreeNode>();
	    	
	    	while (!stack.isEmpty()||root!=null) {//前序的
				if(root!=null)
				{
					qian+=" "+root.val;
					stack.push(root);
					root=root.left;
				}
				else {
					root=stack.pop();
					zhong+=" "+root.val;
					root=root.right;
				}
			}
	    	return qian.substring(1)+zhong;		
	        
	  }
	   static TreeNode Deserialize(String str) {
	    	if(str.equals(""))return null;
	    	String value[]=str.split(" ");
	    	int len=value.length;
	    	int qian[]=new int [len/2];
	    	int hou[]=new int [len/2];
	    	for(int i=0;i<len/2;i++)
	    	{
	    		qian[i]=Integer.parseInt(value[i]);
	    	}
	    	for(int i=0;i<len/2;i++)
	    	{
	    		hou[i]=Integer.parseInt(value[i+len/2]);
	    	}
	    	return creat(qian,hou,0,len/2-1,0,len/2-1);	       
	  }
	    private static TreeNode creat(int[] pre, int[] in, int preleft, int preright, int inleft, int inright) {
			if(preleft>preright||inleft>inright)return null;
			TreeNode node=new TreeNode(pre[preleft]);
			int mid=0;
			for(int i=inleft;i<=inright;i++)
			{
				if(pre[preleft]==in[i])
				{
					mid=i;
				}
			}
			node.left=creat(pre, in, preleft+1, preleft+(mid-inleft), inleft, mid-1);
			node.right=creat(pre, in, preleft+(mid-inleft)+1, preright, mid+1, inright);
		    return node;
		}	
}

參考評論區:
有個遞歸方法挺好的,值得參考:
在這裏插入圖片描述

鏈接:https://www.nowcoder.com/questionTerminal/cf7e25aa97c04cc1a68c8f040e71fb84?answerType=1&f=discussion
來源:牛客網

public class SerializeTree {
 
    int index = -1;
    /**
     * 分別遍歷左節點和右節點,空使用#代替,節點之間,隔開
     *
     * @param root
     * @return
     */
    public String Serialize(TreeNode root) {
        if (root == null) {
            return "#";
        } else {
            return root.val + "," + Serialize(root.left) + "," + Serialize(root.right);
        }
    }
    /**
     * 使用index來設置樹節點的val值,遞歸遍歷左節點和右節點,如果值是#則表示是空節點,直接返回
     *
     * @param str
     * @return
     */
    TreeNode Deserialize(String str) {
        String[] s = str.split(",");//將序列化之後的序列用,分隔符轉化爲數組
        index++;//索引每次加一
        int len = s.length;
        if (index > len) {
            return null;
        }
        TreeNode treeNode = null;
        if (!s[index].equals("#")) {//不是葉子節點 繼續走 是葉子節點出遞歸
            treeNode = new TreeNode(Integer.parseInt(s[index]));
            treeNode.left = Deserialize(str);
            treeNode.right = Deserialize(str);
        }
        return treeNode;
    }
 
    public static void main(String[] args) {
        TreeNode treeNode1 = new TreeNode(1);
        TreeNode treeNode2 = new TreeNode(2);
        TreeNode treeNode3 = new TreeNode(3);
        TreeNode treeNode4 = new TreeNode(4);
        TreeNode treeNode5 = new TreeNode(5);
        TreeNode treeNode6 = new TreeNode(6);
 
        treeNode1.left = treeNode2;
        treeNode1.right = treeNode3;
 
        treeNode2.left = treeNode4;
        treeNode3.left = treeNode5;
        treeNode3.right = treeNode6;
 
        SerializeTree serializeTree = new SerializeTree();
 
        String str = serializeTree.Serialize(treeNode1);
        TreeNode treeNode = serializeTree.Deserialize(str);
    }
}

62 二叉搜索樹第K個節點

題目描述

給定一棵二叉搜索樹,請找出其中的第k小的結點。例如, (5,3,7,2,4,6,8) 中,按結點數值大小順序第三小結點的值爲4。

思路
首先,二叉搜索樹是有序的!二叉搜索樹的中序遍歷是有序序列!(基礎知識和常識了)。所以你主要非遞歸中序把節點釋放到第K個就可以返回結束了。(也可使用遞歸版本)

實現代碼爲:

import java.util.Stack;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
     TreeNode KthNode(TreeNode pRoot, int k)
	    {
	    	if(k==0)return null;
	    	Stack<TreeNode>stack=new Stack<TreeNode>();
	    	while (!stack.isEmpty()||pRoot!=null) {
				if(pRoot!=null)
				{
					stack.push(pRoot);
					pRoot=pRoot.left;
				}
				else {
					pRoot=stack.pop();
					k--;
					if(k==0)return pRoot;
					pRoot=pRoot.right;
				}
			}
	    	if(k>0)return null;
			return pRoot;			
	    }
}

63 數據流中的中位數

題目描述

如何得到一個數據流中的中位數?如果從數據流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從數據流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Insert()方法讀取數據流,使用GetMedian()方法獲取當前讀取數據的中位數。

思路
構造類型的題,中位數,也就是要我們維護一個有序數列取中間數。其實實現的方法也比較多吧。比如你可以用Arraylist。每次加入快速排序。也可以用優先隊列進行堆排序維護。但是快排對已經有序序列效率不高,隊列取數是比較麻煩的。

當然,注意這個序列是你一手構造的,從開始爲0,並且每次加入節點的序列都是有序的。其實插入排序更好,這個步驟就相當於插入排序的最後一部一樣。把最後一個找到合適位置插入就可以了。而序列是有序的,二分法+直接插入是一種很不錯的解法!

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

import java.util.ArrayList;
import java.util.List;
public class Solution {
    List<Integer> list = new ArrayList<Integer>();
   public void Insert(Integer num) {
		    int l=0,r=list.size()-1;
		    int mid=(l+r)/2;
		    while (l<r) {
				if(list.get(mid)<num)
				{
					l=mid+1;
					mid=(l+r)/2;
				}
				else if (list.get(mid)>num) {
					r=mid;
					mid=(l+r)/2;
				}
				else {
					l=r=mid;
				}
			}
		    list.add(l, num);
	    }
	public Double GetMedian() {
	        if(list.size()%2==1)
	        {
	        	return (double)list.get(list.size()/2);
	        }
	        else {
				return ((double) (list.get((list.size()-1)/2)+list.get(list.size()/2))/2);
			}
	    }
}

64 滑動窗口的最大值

題目描述

給定一個數組和滑動窗口的大小,找出所有滑動窗口裏數值的最大值。例如,如果輸入數組{2,3,4,2,6,2,5,1}及滑動窗口的大小3,那麼一共存在6個滑動窗口,他們的最大值分別爲{4,4,6,6,6,5}; 針對數組{2,3,4,2,6,2,5,1}的滑動窗口有以下6個: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路
這題有技巧的,可以直接查找但是可能混亂一點。這題有興趣的可以瞭解線段樹,感覺有些思想有點像。雖然蠻幹和我的題解在效率差別不大,但是這個思想感覺可以體會一下。

  • (1)如果區間爲1,那麼最大的就是每個數。
  • (2)如果區間爲2,就是相鄰的2個進行比較滑動取最大。
  • (3)如果區間爲3,就是相鄰的3個進行比較!還是相鄰的(2)中的相鄰兩個最大兩個滑動。
  • (4)如果區間爲4,就是相鄰的4個進行比較!還是相鄰的(3)中的相鄰兩個最大兩個滑動。
  • -----------

比如序列2 3 4 2 6 2 5 1

  • 區間爲1:2 3 4 2 6 2 5 1
  • 區間爲2:(23)3 (34)4 (42)4 (26)6 (62)6 (25)5 (51)5 7個窗口
  • 區間爲3:(234)3 (342)4 (426)6 (262)6 (625)6 (251)5 6個滑動。也等價於 (2334)3 (3442)4 (4226)6 (2662)6 (6225)6 (2551)5(區間爲2的相互比較)。

當然在空間利用方面,其實不需要開闢新的數組,直接重複利用一個數組就行了,算完一輪進行下一輪,每次長度你記住少一就行了。

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

import java.util.ArrayList;
public class Solution {
   public static ArrayList<Integer> maxInWindows(int [] num, int size)
    {
		 ArrayList<Integer>list=new ArrayList<Integer>();
		if(size==0)return list;
        int len=num.length;
        while (size-->1) {
			for(int i=0;i<len-1;i++)
			{
				num[i]=Math.max(num[i], num[i+1]);
			}
			len--;
		}
        System.out.println(len);
       
        for(int i=0;i<len;i++)
        {
        	list.add(num[i]);
        }
        return list;
    }

}

參考評論區:
評論區的雙隊列方法:

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

/**
     * 雙隊列方法
     * 滑動窗口的最大值總是保存在隊列首部,隊列裏面的數據總是從大到小排列。
     *
     * [@param num
     * @param size
     * @return](/profile/547241) */
    public ArrayList maxInWindows(int[] num, int size) {
        ArrayList res = new ArrayList();
        if (num == null || num.length == 0 || size == 0 || size > num.length) {
            return res;
        }
        Deque deque = new LinkedList();
        for (int i = 0; i < num.length; i++) {
            if (!deque.isEmpty()) {
                // 如果隊列頭元素不在滑動窗口中了,就刪除頭元素
                if (i >= deque.peek() + size) {
                    deque.pop();
                }
                // 如果當前數字大於隊列尾,則刪除隊列尾,直到當前數字小於等於隊列尾,或者隊列空
                while (!deque.isEmpty() && num[i] >= num[deque.getLast()]) {
                    deque.removeLast();
                }
            }
            deque.offer(i); // 入隊列
            // 滑動窗口經過一個滑動窗口的大小,就獲取當前的最大值,也就是隊列的頭元素
            if (i + 1 >= size) {
                res.add(num[deque.peek()]);
            }
        }
        return res;
    }

65 矩陣中的路徑

題目描述

請設計一個函數,用來判斷在一個矩陣中是否存在一條包含某字符串所有字符的路徑。路徑可以從矩陣中的任意一個格子開始,每一步可以在矩陣中向左,向右,向上,向下移動一個格子。如果一條路徑經過了矩陣中的某一個格子,則該路徑不能再進入該格子。 例如 a b c e s f c s a d e e 矩陣中包含一條字符串"bcced"的路徑,但是矩陣中不包含"abcb"路徑,因爲字符串的第一個字符b佔據了矩陣中的第一行第二個格子之後,路徑不能再次進入該格子。

思路
基礎迷宮類dfs搜素題。它給的是一維數組,我們要根據位置對應轉換成二維的數組儲存地圖。爲了方便我們進行搜素。另外要建立X[]Y[]表示上下左右四個移動方向。

而對於這類題我們的一般做法是:

  • 二維的迷宮,用個boolean[][]數組判斷對應位置在當前是否走過。
  • 本題要求找到這麼一串符合的,我們可以遍歷首字母相同的,從首字母相同的進行深度優先搜索,按照方向符合就往下搜索,到整個串路徑存在即可。
  • 本題的搜索是屬於試探回溯的。所以在dfs搜索走完一個位置如果滿足條件,那麼向四個滿足條件(不越界沒走過等)方向進行搜索,同時標記此位置在這條路徑不能再次使用。而這條搜索完需要將boolean數組還原。至於dfs和回溯如果沒基礎的可以百度學習一下。

另外就是注意下特殊值,或者不滿足的要判斷排除。

實現代碼爲:

public class Solution {
     boolean judge=false;//判斷是否存在
	 int x[]= {-1,0,1,0};
	 int y[]= {0,1,0,-1};

	 public  boolean hasPath(char[] matrix, int rows, int cols, char[] str)
	    {
		 if(str.length==0||matrix.length==0)return false;
			char map[][]=new char[rows][cols];
			boolean jud[][]=new boolean[rows][cols];
			for(int i=0;i<matrix.length;i++)//轉到二維數組
			{
				map[i/cols][i%cols]=matrix[i];
			}
			for(int i=0;i<rows;i++)
			{
				for(int j=0;j<cols;j++)
				{
					if(map[i][j]==str[0])//第一個相同
					{
						dfs(map,jud,i,j,0,str);
						if(judge)return true;
					}
				}
			}
			return false;	    
	    }
	private  void dfs(char[][] map, boolean[][] jud, int m, int n, int index, char[] str) {
		if(index==str.length-1) {if(map[m][n]==str[index])judge=true;}//有成功的
		else if(map[m][n]!=str[index]) {}//回溯停止
		else {//map[n][n]==str[index]
			jud[m][n]=true;
			for(int i=0;i<4;i++)
			{
				if(m+x[i]>=0&&m+x[i]<map.length&&n+y[i]>=0&&n+y[i]<map[0].length&&map[m+x[i]][n+y[i]]==str[index+1])
				{
					if(!jud[m+x[i]][n+y[i]])
					{
						dfs(map, jud, m+x[i], n+y[i], index+1, str);
					}
				}
			}
			jud[m][n]=false;
		}		
	}
}

66 機器人的運動範圍

題目描述

地上有一個m行和n列的方格。一個機器人從座標0,0的格子開始移動,每一次只能向左,右,上,下四個方向移動一格,但是不能進入行座標和列座標的數位之和大於k的格子。 例如,當k爲18時,機器人能夠進入方格(35,37),因爲3+5+3+7 = 18。但是,它不能進入方格(35,38),因爲3+5+3+8 = 19。請問該機器人能夠達到多少個格子?

思路
這題也是簡單搜索題,但是處理方式可以用bfs,也可以用dfs主要考察迷宮的通達性。所以你用dfs千萬不要將Boolean判斷數組復原,走過的地方只能走一次。至於這個規則我想很容易判斷,求餘10和除10下去就可以計算每個位置是否滿足規則。

但是如果迷宮和K足夠大的話,那麼橫縱座標都這要這麼計算n次,效率是不高的。你可以預處理的,每一行每一列先加上對應行和列。這樣搜索時候不需要一個個計算直接比較就可以了。當然如果K小或者迷宮小可能會造成一些無用計算,這裏瞭解下就可以了(並未採用)。
在這裏插入圖片描述

實現代碼爲:

public class Solution {
   int X[]= {-1,0,1,0};
	  int Y[]= {0,1,0,-1};
	  int num=0;
	  public int movingCount(int threshold, int rows, int cols)
	    {
		  boolean jud[][]=new boolean[rows][cols];
		  dfs(0,0,rows,cols,threshold,jud);
		  return num;
	    }
	private void dfs(int x, int y, int rows, int cols, int threshold, boolean[][] jud) {
		int count=nadd(x,y);	
		if(count>threshold) { }
		else {
			num++;
			jud[x][y]=true;
			for(int i=0;i<4;i++)
			{
				if(x+X[i]>=0&&x+X[i]<rows&&y+Y[i]>=0&&y+Y[i]<cols&&!jud[x+X[i]][y+Y[i]])//不越界
				{
					dfs(x+X[i], y+Y[i], rows, cols, threshold, jud);
				}
			}
		}
	}
	private int nadd(int x, int y) {//計算個位數字之和
		int num=0;
		while (x>0) {
			num+=x%10;x/=10;
		}
		while (y>0) {
			num+=y%10;y/=10;
		}
		return num;
	}

}

67 剪繩子

題目描述

給你一根長度爲n的繩子,請把繩子剪成整數長的m段(m、n都是整數,n>1並且m>1),每段繩子的長度記爲k[0],k[1],…,k[m]。請問k[0]xk[1]x…xk[m]可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到的最大乘積是18。
輸入描述:
輸入一個數n,意義見題面。(2 <= n <= 60)

輸出描述:

輸出答案。
示例1
輸入
8
輸出
18

思路
數據範圍很小,還沒看題解過得,盲猜貪心思路!談談靈感吧(不一定對)!

  • 如果分成兩份,兩個相同的乘積應該最大
  • 如果分成n份,n個相同乘積應該最大
  • 分成幾份?我猜對於一個數N,mm=n這個應該是最優等分。但是m只能取整數,所以一定在m取整的左右之間!好了,n<60這個範圍太小。很好搞。也就分成幾個區間而已.甚至2和3段分法能數出來是7.3*4=12,2*3*3=12.所以7到27肯定3段。27-60在3和4不確定,比較一下就行了。

知道分段n均分會求吧!這裏有個小技巧可以參考代碼。

 int a[]= {1,4,27,128};//1 2*2 3*3*3 4*4*4*4    2-7分兩個  7-27分3   27-60分3或者4嘗試

實現代碼爲:

public class Solution {
  public int cutRope(int target) {
		  int a[]= {1,4,27,128};//1 2*2  3*3*3  4*4*4     2-7分兩個     7-27分3    27-60分3或者4嘗試
		  if(target<=7)
			  return (target/2)*(target-target/2);
		  else if (target<=27) {
			return (target/3)*((target+1)/3)*((target+2)/3);//看看餘數要分配給幾個數
		}
		  else {
			return Math.max((target/3)*((target+1)/3)*((target+2)/3), (target/4)*((target+1)/4)*((target+2)/4)*((target+3)/4));
		}	    
	  }
}

參考評論區
看了評論區才發現原來這題分析的正解是什麼,前面的自己做法雖然A了但可能是錯誤,但是這個想法仍有部分參考價值吧

主流解法是貪心和dp吧。個人更推薦貪心感覺更容易理解。

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

//
// Created by yuanhao on 2019-9-3.
//
 
#include <iostream>
#include <cmath>
 
using namespace std;
 
/**
 * 題目分析:
 * 先舉幾個例子,可以看出規律來。
 * 4 : 2*2
 * 5 : 2*3
 * 6 : 3*3
 * 7 : 2*2*3 或者4*3
 * 8 : 2*3*3
 * 9 : 3*3*3
 * 10:2*2*3*3 或者4*3*3
 * 11:2*3*3*3
 * 12:3*3*3*3
 * 13:2*2*3*3*3 或者4*3*3*3
 *
 * 下面是分析:
 * 首先判斷k[0]到k[m]可能有哪些數字,實際上只可能是2或者3。
 * 當然也可能有4,但是4=2*2,我們就簡單些不考慮了。
 * 5<2*3,6<3*3,比6更大的數字我們就更不用考慮了,肯定要繼續分。
 * 其次看2和3的數量,2的數量肯定小於3個,爲什麼呢?因爲2*2*2<3*3,那麼題目就簡單了。
 * 直接用n除以3,根據得到的餘數判斷是一個2還是兩個2還是沒有2就行了。
 * 由於題目規定m>1,所以2只能是1*1,3只能是2*1,這兩個特殊情況直接返回就行了。
 *
 * 乘方運算的複雜度爲:O(log n),用動態規劃來做會耗時比較多。
 */
long long n_max_3(long long n) {
    if (n == 2) {
        return 1;
    }
    if (n == 3) {
        return 2;
    }
    long long x = n % 3;
    long long y = n / 3;
    if (x == 0) {
        return pow(3, y);
    } else if (x == 1) {
        return 2 * 2 * (long long) pow(3, y - 1);
    } else {
        return 2 * (long long) pow(3, y);
    }
}
 
//給你一根長度爲n的繩子,請把繩子剪成m段(m、n都是整數,n>1並且m>1),每段繩子的長度記爲k[0],k[1],...,k[m]。請問k[0]xk[1]x...xk[m]可能的最大乘積是多少?例如,當繩子的長度是8時,我們把它剪成長度分別爲2、3、3的三段,此時得到的最大乘積是18。
//
//輸入描述:
//輸入一個數n,意義見題面。(2 <= n <= 100)
//
//
//輸出描述:
//輸出答案。
//示例1
//輸入
//8
//輸出
//18
int main() {
    long long n = 0;
    cin >> n;
    cout << n_max_3(n) << endl;
    return 0;
}
鏈接:https://www.nowcoder.com/questionTerminal/57d85990ba5b440ab888fc72b0751bf8?f=discussion
來源:牛客網

 /**
     * @param target
     * @return
     *
     * 分類:最值型、劃分型
     *
     * 考慮最後一步:
     * 必然有一個點,把target分成兩段,兩段分別構成最小子問題。
     * 兩段的最大值的乘積,也是target所求的最大值。
     * 設劃分點爲i,f[i]表示長度爲i的繩子的乘積最大值。
     *
     * 轉移方程:
     * f[i] = MAX{f[j]*f[i-j]}|0<j<i
     *
     * 那麼我們求的就是f[target]
     *
     * 初始值:
     * f[0]=1
     * f[1]=1
     *
     * 邊界情況:
     * 無
     *
     * 計算順序:
     * i從1到target
     * j從1到i
     */
    public int cutRope(int target) {
        int[] f = new int[target+1];
        //初始化
        f[0] = 0;
        f[1] = 1;
        for (int i = 1; i <= target; i++) {
            /**
             * 處理不分割的情況,因爲繩子不能不被分割
             */
            if(i==target) {
                f[i] = 1;
            }else {
                f[i] = i;
            }
            for (int j = 1; j < i; j++) {
                f[i] = Math.max(f[i],f[j]*f[i-j]);
            }
        }
        return f[target];
    }

到這裏,劍指offer就搞完了。2020-01-19到2020-02-29(大年初五),特記!歡迎關注公衆號:bigsai下次再一起玩!

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