面試題三:查找二維數組中元素問題
public static void main(String[] args){ int[][] num = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}}; search(num,7); } public static void search(int[][] arr,int target){ int rows = arr.length; int columns = arr[0].length; int row = 0; int column = columns-1; while(row<=rows&&column>=0){ if(target==arr[row][column]){ System.out.println(target+"在第"+row+"行,第"+column+"列"); break; } if(target>arr[row][column]){ row++; } if(target<arr[row][column]){ column--; } } }
面試題四:替換字符串中的空格
延伸:1.合並兩個字符串 2.兩個有序數組,將一個插入到另一個,並保證有序。 從后面開始會減少元素移動的次數?
public static void main(String[] args){ int[][] num = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}}; String str = "we are happy"; replaceBlank(str); } public static void replaceBlank(String str){ char[] charOld = str.toCharArray(); char[] charNew = new char[100]; for(int j = 0;j<charOld.length;j++){ charNew[j] = charOld[j]; } int blank = 0; for(int i = 0;i<charNew.length;i++){ if(charNew[i]==' '){ blank++; } } int lengthFront = charOld.length-1; int lengthBack = charOld.length+2*blank-1; while(lengthFront>=0&&lengthBack>=0){ if(charNew[lengthFront]!=' '){ charNew[lengthBack--] = charNew[lengthFront]; } else { charNew[lengthBack--] = '0'; charNew[lengthBack--] = '2'; charNew[lengthBack--] = '%'; lengthFront--; } lengthFront--; } System.out.println(charNew); }
面試題5.從尾到頭打印鏈表(利用棧或遞歸來實現)
構建鏈表
public class ListNode { private int value; private ListNode next; public ListNode(int value){ this.value = value; } public ListNode(int value,ListNode next){ this.value = value; this.next = next; } public void setValue(int value){ this.value = value; } public int getValue(ListNode node){ return node.value; } public void setNext(ListNode next){ this.next = next; } public ListNode getNext(){ return this.next; } }
Stack s = new Stack(); 棧 public static void method(head){ 遞歸,但是鏈表長度較長時就不要用
ListNode p = head; if(head!=null){
while(p!=null){ if(head.getNext()!=null){
stack.push(p.getValue()); method(head.getNext);
p=p.getNext(); }
} System.out.println(head.getValue());
while(!s.isEmpty){ }
System.out.println(s.pop()); }
}
面試題六:根據前序和中序輸出構造二叉樹
建二叉樹
public class BinaryTreeNode { private int value; private BinaryTreeNode left; private BinaryTreeNode right; public BinaryTreeNode(int value){ this.value = value; } public BinaryTreeNode(int value,BinaryTreeNode left,BinaryTreeNode right){ this.value = value; this.left = left; this.right = right; } public int getValue( ){ return this.value; } public void setValue(BinaryTreeNode node){ this.value = value; } public void setLeft(BinaryTreeNode node){ this.left = node; } public void setRight(BinaryTreeNode node){ this.right = node; } public BinaryTreeNode getLeft( ){ return this.left; } public BinaryTreeNode getRight( ){ return this.right; } }
根據前序找根節點,然后判斷根節點在中序輸出中的位置,根節點左邊的就是左子樹,右邊的就是右子樹,然后遞歸調用此方法。
public static void main(String[] args){ int[][] num = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}}; int[] frontOrder = {1,2,4,7,3,5,6,8}; int[] inOrder = {4,7,2,1,5,3,8,6}; BinaryTreeNode root =BinaryTree(frontOrder,inOrder); printPostOrder(root); } public static void printPostOrder(BinaryTreeNode root){ if(root!=null){ printPostOrder(root.getLeft( )); printPostOrder(root.getRight( )); System.out.println(root.getValue()); } } public static BinaryTreeNode BinaryTree(int[] frontOrder,int[] inOrder){ BinaryTreeNode root = new BinaryTreeNode(frontOrder[0]); root.setLeft(null); root.setRight(null); int leftLength = 0; for(int i =0;i<inOrder.length;i++){ if(inOrder[i]==root.getValue( )){ break; }else{ leftLength++; } } int rightLength = inOrder.length-leftLength-1; if(leftLength>0){ int[] leftFrontOrder = new int[leftLength]; int[] leftInorder = new int[leftLength]; for(int j = 0;j<leftLength;j++){ leftFrontOrder[j] = frontOrder[j+1]; leftInorder[j] = inOrder[j]; } BinaryTreeNode leftRoot = BinaryTree(leftFrontOrder,leftInorder); root.setLeft(leftRoot); } if(rightLength>0){ int[] rightFrontOrder = new int[rightLength]; int[] rightInorder = new int[rightLength]; for(int k = 0;k<rightLength;k++){ rightFrontOrder[k] = frontOrder[k+1+leftLength]; rightInorder[k] = inOrder[k+1+leftLength]; } BinaryTreeNode rightRoot =BinaryTree(rightFrontOrder,rightInorder); root.setRight(rightRoot); } return root; }
面試題七.兩個棧實現一個隊列
先用一個棧存,這樣就是倒序,然后依次取出放入另一個棧,這就是正序了,然后再取出,就和隊列一樣了。
public class sQueue<T> { Stack<T> s1 = new Stack<T>(); Stack<T> s2 = new Stack<T>(); public void appendTrail(T append){ s1.push(append); } public T deleteHead(Stack<T> s){ if(s1==null){ if(s2==null){ try{ throw new Exception("隊列為空"); }catch(Exception e ){ e.printStackTrace(); } } } while(s1.size()>0){ s2.push(s1.pop()); } return s2.pop(); } }
面試題:將企業中員工年齡排序,使用一個長度100的數組作為輔助空間,記錄每個年齡出現的次數,然后按照這個記錄的次數輸出年齡
public static void sort(int[] ages,int length){ int largeAge = 99; int[] timeAge = new int[largeAge+1]; length = ages.length; if(ages==null||length<0){ return; } for(int i = 0;i<length;i++){ int age = ages[i]; if(ages[i]<0||ages[i]>99){ return; } timeAge[age]++; } int index = 0; for(int j = 0;j<=largeAge;j++){ for(int k =0;k<timeAge[j];k++){ ages[index] = j; index++; } } }
面試題八.求旋轉數組的最小值
public static int findMin(int[] num){ if(num==null||num.length<=0){ return 0; } int middle = num.length/2; int front = 0; int back = num.length-1; while(num[front]>num[back]){ if(back-front==1){ middle = back; break; } middle = (front+back)/2; if(num[middle]==num[front]&&num[front]==num[back]){ 10111 這種情況就需要順序遍歷來找了 return 0; } if(num[middle]>num[front]){ front = middle; } if(num[middle]<num[front]){ back = middle; } } return num[middle]; }
面試題九:斐波那契數列
f(n) = f(n-1)+f(n-2) 類似的有上台階問題,一次能邁1個或2個台階,問有多少種方法上樓梯。邁上終點之前肯定是邁了一步或是兩步,那n-1個台階的方法數加上n-2個台階的方法數就是n個台階的方法數。
那么如果一次能邁的台階數沒有限制呢,1,2,3,。。。n 用歸納法得出f(n) = 2的n-1次方
public static int fibonaci(int n){ if(n==0){ return 0; }else if(n==1){ return 1; } else{ int front = 0; int back =1; int x =0; for(int i =0;i<=n;i++){ x = front+back; front = back; back =x; } return x; } }
類似的格子填充問題
面試題十:二進制中1的個數
正數(1,0x7FFFFFFF)負數(0x80000000,0xFFFFFFFF)
解法:十進制中的1二進制表示為00000001,我們可以借助它和要檢驗的二進制數A相與,這樣,就可以根據結果獲知 A中最后一位是0還是1。
1.第一次相與后,把A右移一位,再次相與,這樣依次右移就可以知道每一位是0是1。但是當A為負數時這種方法不可行。移位前是負數的話,就要保證移位后是負數,這樣移位后最高位自動置1.
2.第一次相與后,將00000001依次向左移動一位,這是一種較好的方法,因為不確定A的大小,移動可能會產生影響。
3.最好的解法:比如一個1100,將1100減去1是1011,然后將1100和1011相與得出1000,這樣1100中最右邊的1就變為0。再執行一次的話就變成0000。這樣執行n次后A變為0,那么A中就有n個1.
把一個二進制A和B=(A-1)相與,那么A的最后一個1變0.
public static void main(String[] args){ int n =10; int c = count(n); System.out.println(c); } public static int count(int n){ int num = 0; while(n!=0){ n = n&(n-1); num++; } return num; }
相關題目1:一個數是否為2的整數次方。
解法:一個數如果是2的整數次方,那么它的二進制有且只有一個1.
相關題目2:求從一個二進制變為另一個需要改變多少位。
解法:兩數異或,統計二進制結果中1的數目。
面試題11.數的整數次冪
base為0,指數為負的情況也要考慮到。 指數為負,把指數取正,然后結果取倒數。
public static void main(String[] args){
double a = power(2,5);
System.out.println(a);
}
public static double power(double base,int exponent){
if(exponent ==0 ){
return 1;
}
if(exponent == 1){
return base;
}
if(exponent>>1==0){
int exponent1 = exponent>>1;
double result = power(base,exponent1);
return result*result;
}else{
int exponent2 = exponent-1;
double result = power(base,exponent2);
return result*base;
}
}
面試題十二:輸出1到n位最大整數
如果按照最簡單的循環輸出,會遇到邊界問題,n非常大的話,int甚至long都不能滿足需求,所以這里需要用數組或者是字符串來表示要輸出的數字。
如果面試題給定了一個n位整數,那么就是大數問題,用字符串來解決。
給定兩個整數相加求結果,也是大數問題。
public static void main(String[] args){ bigData(3); } public static void bigData(int n){ char[] num = new char[n]; for(int i = 0;i<n;i++){ num[i] = '0'; } boolean end = false; while(!end){ num[n-1]++; for(int k =n-1;k>0;k--){ if(num[k]=='9'+1){ num[k] = '0'; num[k-1]++; } } if(num[0]=='9'+1){ end = true; break; } boolean out = false; for(int j =0;j<n;j++){ if(num[j]=='0'&&!out){ //out是為了避免像100這樣的數字,后邊的兩個0不會輸出,當遇到第一個非0數字后,改變end狀態,就不會進入忽略0的語句。 continue; }else{ out = true; System.out.print(num[j]); } } System.out.println("..."); }
}
面試題十三:在O(1)時間內刪除單向鏈表中的一個節點
思路:如果從首部開始依次查找,那么時間是O(n).
既然我們知道要刪除的結點i,那么我們就知道它指向的下一個結點j,那么我們可以將j的內容復制到i,然后將i的指針指向j的下一個結點,這樣雖然看起來我們刪除的是j結點,但是實際刪除的是i。
此外還要考慮的問題是:如果結點不存在怎么辦?如果結點是尾結點怎么辦?鏈表只有一個結點?
public class deleteInode { public static void main(String[] args) { ListNode head = new ListNode(0); ListNode node1 = new ListNode(1); ListNode node2 = new ListNode(2); ListNode node3 = new ListNode(3); head.setNext(node1); node1.setNext(node2); node2.setNext(node3); delete(head,node2); printListNode(head); } public static void delete(ListNode head,ListNode target){ if(head==null||target==null){ return; } if(head.getNext()==null){ if(head==target){ head=null; }else{ return; } } if(target.getNext()==null){ ListNode currentNode = head; while(currentNode.getNext()!=null){ currentNode = currentNode.getNext(); } currentNode.setNext(null); } if(target.getNext()!=null){ target.setValue(target.getNext().getValue()); if(target.getNext().getNext()!=null){ target.setNext(target.getNext().getNext()); }else{ target.setNext(null); } } } public static void printListNode(ListNode head){ ListNode current = head; while(current!=null){ System.out.println(current.getValue()+"..."); current = current.getNext(); } } }
面試題十五.鏈表中倒數第K個結點
思路:兩個指針A、B,最開始都指向第一個結點,先讓A向前走k-1步,然后從第K步開始,A和B同時向前走,這樣,當A到達最后一個結點時,B的位置就是倒數第K個結點。
public static ListNode findKNode(ListNode head,int k){ if(head==null){ return null; //重要!!!魯棒性的判斷 } if(k<1){ return null; } ListNode firstNode = head; ListNode secondNode = head; for(int i =0;i<k-1;i++){ if(firstNode.getNext()!=null){ firstNode = firstNode.getNext(); }else{ return null; } } while(firstNode.getNext()!=null){ firstNode = firstNode.getNext(); secondNode = secondNode.getNext(); } return secondNode; }
相關問題1:求鏈表的中間結點
解決方法:兩個指針,第一個一次走兩步,第二個一次走一步,第一個到達終點時,第二個到達中點。
相關問題2:環形鏈表問題
解決方法:也是兩個指針不一樣的速度走,如果第一個指針到達終點(getNext()等於空)時都沒有碰到第二個,那么就不是環形鏈表。
注:環形鏈表可以是首尾相連(O型),也可以是尾部和中間的某個結點相連(6型)。
面試題十六:反轉鏈表
反轉鏈表相當於喊向后轉的口號之后,隊首變隊尾,隊尾變隊首。
下圖說明了存在的一個隱患,那就是當把(i)結點的指針指向h時,這時如果沒有提前將(j)結點存儲,那么下一步就找不到(j)結點了,鏈表會斷裂。
所以我們要聲明三個變量,分別記錄h、i、j,對應上一個、當前、下一個。
public class Test { public static void main(String[] args){ ListNode head = new ListNode(0); ListNode node1 = new ListNode(1); ListNode node2 = new ListNode(2); ListNode node3 = new ListNode(3); head.setNext(node1); node1.setNext(node2); node2.setNext(node3); ListNode node = reverseNode(head); print(node); } public static ListNode reverseNode(ListNode head){ if(head==null){ return null; } ListNode preNode = null; ListNode curNode = head; ListNode nextNode = null; ListNode reverseNode = null; while(curNode!=null){ nextNode = curNode.getNext(); if(nextNode==null){ reverseNode = curNode; } curNode.setNext(preNode); preNode = curNode; curNode = nextNode; } return reverseNode; } public static void print(ListNode head){ ListNode current = head; while(current!=null){ System.out.println(current.getValue()); current = current.getNext(); } } }
面試題十七:合並兩個已排序的鏈表
其實這是一個遞歸問題,在比較兩個鏈表的頭結點后,選定其中一個做為新鏈表的結點,那么產生下一個結點的過程和最開始一樣,兩個鏈表的頭結點中選擇一個做為新鏈表的下一個結點,所以是遞歸問題。
public class Test { public static void main(String[] args){ ListNode head1 = new ListNode(1); ListNode node1 = new ListNode(3); ListNode node2 = new ListNode(5); ListNode node3 = new ListNode(7); head1.setNext(node1); node1.setNext(node2); node2.setNext(node3); ListNode head2 = new ListNode(2); ListNode node4 = new ListNode(4); ListNode node5 = new ListNode(6); ListNode node6 = new ListNode(8); head2.setNext(node4); node4.setNext(node5); node5.setNext(node6); ListNode head = merge(head1,head2); print(head); } public static ListNode merge(ListNode headA,ListNode headB){ ListNode nodeA = headA; ListNode nodeB = headB; ListNode head = null; if(headA==null&&headB!=null){ head = headB; } if(headB==null&&headA!=null){ head = headA; } if(headA==null&&headB==null){ return null; } if(nodeA!=null&&nodeB!=null){ if(nodeA.getValue()<nodeB.getValue()){ head = nodeA; head.setNext(merge(nodeA.getNext(),nodeB)); }else{ head = nodeB; head.setNext(merge(nodeA,nodeB.getNext())); } } return head; } public static void print(ListNode head){ ListNode curNode = head; while(curNode!=null){ System.out.println(curNode.getValue()); curNode = curNode.getNext(); } } }
面試題十八:樹的子結構
兩個遞歸方法,一個是尋找相同的結點,如果遇到相同結點,就調用比較左右子結點的方法。如果根節點和目標結點不相同,就比較左右子結點和目標是否相同,相同,就調用比較子結點方法,不相同,繼續調用尋找相同結點方法。
public class Test { public static void main(String[] args){ BinaryTreeNode root = new BinaryTreeNode(8); BinaryTreeNode node1 = new BinaryTreeNode(8); BinaryTreeNode node2 = new BinaryTreeNode(7); BinaryTreeNode node3 = new BinaryTreeNode(9); BinaryTreeNode node4 = new BinaryTreeNode(2); root.setLeft(node1); root.setRight(node2); node1.setLeft(node3); node1.setRight(node4); BinaryTreeNode target = new BinaryTreeNode(8); BinaryTreeNode node5 = new BinaryTreeNode(9); BinaryTreeNode node6 = new BinaryTreeNode(2); target.setLeft(node5); target.setRight(node6); boolean result2 = findSame(root,target); System.out.println(result2); } public static boolean findSame(BinaryTreeNode root,BinaryTreeNode target){ boolean result = false; if(root!=null&&target!=null){ if(root.getValue()==target.getValue()){ result = sameTree(root,target); } if(!result){ result = findSame(root.getLeft(),target); } if(!result){ result = findSame(root.getRight(),target); } } return result; } public static boolean sameTree(BinaryTreeNode node,BinaryTreeNode target){ if(target==null){ return true; } if(node ==null ){ return false; } if(node.getValue()!=target.getValue()){ return false; } return sameTree(node.getLeft(),target.getLeft())&&sameTree(node.getRight(),node.getRight()); } }
面試題十九:二叉樹鏡像
貌似對於二叉樹的整體操作都是遞歸問題,因為操作過根節點之后,左右兩顆子樹就可以看成單獨的樹遞歸操作。
打印二叉樹的時候,每次打印根結點就可以,因為每個結點輸出之后,它的子結點都可以看作根結點。
public class Test {
public static void main(String[] args){
BinaryTreeNode root = new BinaryTreeNode(8);
BinaryTreeNode node1 = new BinaryTreeNode(8);
BinaryTreeNode node3 = new BinaryTreeNode(9);
BinaryTreeNode node2 = new BinaryTreeNode(7);
BinaryTreeNode node4 = new BinaryTreeNode(6);
root.setLeft(node1);
root.setRight(node3);
node1.setLeft(node2);
node3.setRight(node4);
mirrorBinary(root);
printBinaryTree(root);
}
public static void mirrorBinary(BinaryTreeNode root){
if(root == null){
return;
}
if(root.getLeft()==null&&root.getRight()==null){
return;
}
BinaryTreeNode temp = root.getLeft();
root.setLeft(root.getRight());
root.setRight(temp);
if(root.getLeft()!=null){
mirrorBinary(root.getLeft());
}
if(root.getRight()!=null){
mirrorBinary(root.getRight());
}
}
public static void printBinaryTree(BinaryTreeNode root){
if(root!=null){
System.out.println(root.getValue());
printBinaryTree(root.getLeft());
printBinaryTree(root.getRight());
}
}
}
按照循環的方法做,就要用到隊列了, 利用隊列的先進先出的性質,依次添加所有結點,在取出每個結點時,並不對結點操作,而是對結點的兩個子結點進行添加進隊列和交換的操作。
這里利用隊列的方法類似於二叉樹的分層遍歷所采用的方法。
public static void mirrorBinary(BinaryTreeNode root){ Queue<BinaryTreeNode> q = new LinkedList<BinaryTreeNode>(); BinaryTreeNode temp = new BinaryTreeNode(0); if(root!=null){ q.add(root); } while(q.size()>0){ BinaryTreeNode node = q.poll(); if(node.getLeft()!=null){ q.add(node.getLeft()); } if(node.getRight()!=null){ q.add(node.getRight()); } temp = node.getLeft(); node.setLeft(node.getRight()); node.setRight(temp); } }
面試題二十:順時針打印矩陣(按圈打印)
public class Test { public static void main(String[] args){ BinaryTreeNode root = new BinaryTreeNode(8); int[][] num = {{1,2,3,4},{5,6,7,8,},{1,3,5,7,},{2,4,6,8}}; print(num,4,4); } public static void print(int[][] num,int rows,int columns){ if(num==null||rows<=0||columns<=0){ return; } int temp = 0; while(rows>temp*2&&columns>temp*2){ printCircle(num,temp,rows,columns); temp++; } } private static void printCircle(int[][] num,int start,int rows,int columns) { int endRow = rows-1-start; int endColumn = columns-1-start; for(int i = start;i<=endColumn;i++){ System.out.print(num[start][i]); } //如果行數大於起始值,那么肯定不止一行,所以最右一列可以打印 if(start<endRow){ for(int i = start+1;i<=endRow;i++){ System.out.print(num[i][endRow]); } } //從右到左打印最下一行 if(start<endRow&&start<endColumn){ for(int i = endColumn-1;i>=start;i--){ System.out.print(num[endRow][i]); } } //打印最左邊一列 if(start<endColumn&&start<endRow-1){ for(int i =endRow-1;i>start;i--){ System.out.print(num[i][start]); } } System.out.println("......."); } }
面試題二十一:包含min函數的棧
創建一個輔助棧,在每次存入新元素時,將新元素和輔助棧的棧頂元素相比,如果棧頂元素小,則再添加一次棧頂元素,否則添加新元素。這樣可以保證輔助棧的棧頂始終都是原本棧中的最小元素。
public class minStack { private Stack<Integer> stack1; private Stack<Integer> stackHelp; private int temp,pop1,pop2; public minStack(){ stack1 = new Stack<Integer>(); stackHelp = new Stack<Integer>(); } public void push(int num){ stack1.push(num); if(stackHelp.size()==0||num<stackHelp.peek()){ stackHelp.push(num); }else{ stackHelp.push(stackHelp.peek()); } } public void pop(){ pop1 = stack1.pop(); pop2 = stackHelp.pop(); System.out.println("本棧是"+pop1+"輔助棧是"+pop2); } public void min(){ System.out.println("最小值是"+stackHelp.pop()); } }
面試題二十二:棧的壓入,彈出序列
輸入兩組數,判斷一個是否是另一個的彈棧順序。比如A{1,2,3,4,5} B{1,2,5,3,4} 在依次往A中壓入元素時,不斷比較棧頂元素,1,2,3,當A棧中4進入時,等於B中棧頂元素,那么4彈出。再比較A和B的棧頂元素,都是3,3出棧。然后A是2,B是5,不相同,則5進入A棧,此時棧頂元素相同,5出棧。然后就是2,1. 則第二個數組滿足條件。
public class Test { public static void main(String[] args){ int[] numPush = {1,2,3,4,5}; int[] numPop = {4,3,5,2,1}; System.out.println(isPopOrder(numPush,numPop)); } public static boolean isPopOrder(int[] numPush,int[] numPop ){ if(numPush.length<=0||numPop.length<=0||numPush.length!=numPop.length){
return false;
} Stack<Integer> stackPop = new Stack<Integer>(); for(int i=numPop.length-1;i>=0;i--){ stackPop.push(numPop[i]); } Stack<Integer> stackPush = new Stack<Integer>(); int j = 0; while(j<numPush.length){ if(numPush[j]!=stackPop.peek()){ stackPush.push(numPush[j]); j++; }else{ stackPush.push(numPush[j]); System.out.println(numPush[j]); while(stackPush.size()>0&&stackPop.size()>0&&stackPush.peek()==stackPop.peek()){ System.out.println("..."+stackPush.pop()); System.out.println(".."+stackPop.pop()); } j++; } } if(stackPush.size()==0){ return true; }else{ return false; } } }
面試題二十四:判斷一個數組是否是某二叉樹的后序遍歷順序
后續遍歷結果最后一個是根節點,除去根節點,數組的前半部分應該都比根節點的值小,數組的后半部分應該都比根節點的值大,按照這個規律,遞歸判斷。
int[] left = Arrays.copyOfRange(num, 0, i); 從第0位截取到i-1位。
public static boolean isBinaryTree(int[] num ){ if(num.length<=0){ return false; } int root = num[num.length-1]; int i = 0 ; for(;i<num.length-1;i++){ if(num[i]>root){ break; } } for(int j =i;j<num.length-1;j++){ if(num[j]<root){ return false; } } int[] left = Arrays.copyOfRange(num, 0, i); int[] right = Arrays.copyOfRange(num, i, num.length-1); Boolean isLeft = true; if(left.length>0){ isBinaryTree(left); } Boolean isRight = true; if(right.length>0){ isBinaryTree(right); } return(isLeft&&isRight); }
面試題二十五:二叉樹中和為某一值的路徑
public static void main(String[] args){ BinaryTreeNode root = new BinaryTreeNode(5); BinaryTreeNode node1 = new BinaryTreeNode(4); BinaryTreeNode node2 = new BinaryTreeNode(3); BinaryTreeNode node3 = new BinaryTreeNode(5); BinaryTreeNode node4 = new BinaryTreeNode(1); BinaryTreeNode node5 = new BinaryTreeNode(2); BinaryTreeNode node6 = new BinaryTreeNode(1); root.setLeft(node1); root.setRight(node3); node1.setLeft(node2); node3.setLeft(node4); node3.setRight(node5); node4.setLeft(node6); Stack<Integer> stack = new Stack<Integer>(); isPath(12,0,root,stack); } public static void isPath(int expectSum,int currentSum,BinaryTreeNode root,Stack<Integer> stack){ if(root==null){ return; } stack.push(root.getValue()); currentSum += root.getValue(); if(root.getLeft()==null&&root.getRight()==null&¤tSum==expectSum){ for(Integer e : stack){ //這種方式輸出不用談棧,元素還在棧里 System.out.print(e+"\t"); } System.out.println(); } if(root.getLeft()!=null){ isPath(expectSum,currentSum,root.getLeft(),stack); } if(root.getRight()!=null){ isPath(expectSum,currentSum,root.getRight(),stack); } stack.pop(); //執行到葉結點(倒數第二層的左子結點)之后,pop,就會回到上一層,然后判斷一下有沒有右子結點,執行下一步沒有的話,再pop,又往上一層,再檢查右子結點。 }
面試題二十六:復制一個復雜鏈表
復雜鏈表:一個結點可以有兩個指向,一個是next,一個是亂序
1.首先只復制next鏈表,把復制結點放到原本結點的后邊。
2.復制亂序指向
3.分離兩個鏈表,奇數位的連在一起,偶數位的連在一起,就是兩個相同的鏈表。
分離時,使用兩個指針,一個pNode,一個pCloneNode,將pNode的next指向pClonedNode的next之后,移動pNode到第三個,然后將pCloneNode指向pNode的next,然后移動pCloneNode到第四個,這樣不斷移動,實現分離。
面試題二十七:二叉搜索樹轉換成有序雙向鏈表
中序遍歷二叉樹得到的結果是有序的。
1,curNode記錄的是上一個結點,當root行進到最左邊葉結點的左子結點時,root為空,程序不執行,退回上一步。上一步的root輸入是最左邊的葉結點。
2,這時從第5行執行,但這時的curNode為空。第9行:curNode = 最左葉結點。第十行:遞歸最左子結點的右子結點,如果存在,那么驗證是否有左子結點,沒有,返回上一層執行第5行,和curNode互指。
3,處理完curNode的子結點,就要返回上一層,這時的root輸入為curNode的父結點,執行第5行互指。然后curNode被設置成root。再去尋找root的右子結點。
過程中,由於程序的對於二叉樹中結點的遍歷順序是左中右,所以curNode的指向也是這個順序。在每一次退出上一層時,curNode的指向都會更新。
程序最開始位於左中右的中,然后尋找右,這里的中和右其實都是上一層的左。返回上一層的中,尋找上一層的右。
1 private BinaryTreeNode curNode ; 2 public void toTwo(BinaryTreeNode root){ 3 if(root!=null){ 4 toTwo(root.getLeft()); 5 if(curNode!=null){ 6 curNode.setLeft(root); 7 root.setRight(curNode); 8 } 9 curNode = root; 10 toTwo(root.getRight()); } }
面試題二十八:字符串的排列
和八皇后問題一樣,都是用 回溯法解決問題。
public class Test { private final int SET = 1; private final int UNSET = 0; private int size; private int[] set; private char[] c; private char[] location; private int count; public Test(int size,char[] c){ this.size = size; this.c = c; location = new char[size]; set = new int[size]; } public void charSet(int i,int j,int k){ location[i] = c[j]; if(k==0){ set[j] = UNSET; } } public int isPlace(int j){ return set[j]; } public void print(){ System.out.println("第"+count+"種方法"); for(int j=0;j<size;j++){ System.out.print(location[j]+"."); } System.out.println(); } public void place(int i){ for(int j = 0;j<size;j++){ if(set[j]==UNSET){ charSet(i,j,1); set[j] = SET; if(i<size-1){ place(i+1); }else{ count++; print(); } charSet(i,j,0); } } } public static void main(String[] args){ char[] c = {'a'}; if(c==null){ return; } else{ Test t = new Test(c.length,c); t.place(0); } } }
擴展:求字符的所有組合Input:abc Output:a,b,c,ab,bc,ac,abc
public class Test { public static void main(String[] args){ perm("abc"); } public static void perm(String s){ List<String> result = new ArrayList<String>(); for(int i =1;i<=s.length();i++){ perm(s,i,result); } } public static void perm(String s,int m,List<String> result){ if(s.length()<m){ return; } if(m==0){ for(int i =0;i<result.size();i++){ System.out.println(result.get(i)); } System.out.println(); return; }else{ if(s.length()!=0){ result.add(s.charAt(0)+""); perm(s.substring(1,s.length()),m-1,result); result.remove(result.size()-1); perm(s.substring(1,s.length()),m,result); } } } }