剑指offer编程题java实现(正在更新)
面试题三:查找二维数组中元素问题
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); } } } }