一、引言
《剑指offer》可谓是程序猿面试的神书了,在面试中帮了我很多,大部分面试的算法题都会遇到原题或者是类似的题。但是书上的代码都是C版的,我在这里整理了一份Java版的代码供大家学习参考,这些代码我都是在OJ上跑过全AC的,所以正确性你大可放心。
二、目录
1赋值运算函数
2单例设计模式
3二维数组中查找目标值
4替换字符串中的空格
5从尾到头打印链表
6由前序和中序遍历重建二叉树
7用两个栈实现队列
8求旋转数组的最小数字
9斐波那契数列的第n项(青蛙跳台阶)
10二进制中1的个数
11数值的整数次方
12打印1到最大的n位数
13O(1)时间删除链表节点
14使数组中的奇数位于偶数前面
15找链表中倒数第K个节点
16输出反转后的链表
17合并两个有序链表
18判断二叉树A中是否包含子树B
19二叉树的镜像
20顺时针打印矩阵
21包含min函数的栈
22判断一个栈是否是另一个栈的弹出序列
23层序遍历二叉树
24后序遍历二叉搜索树
25二叉树中和为某值的路径
26复杂链表的复制
27二叉搜索树转换为双向链表
28打印字符串中所有字符的排列
29数组中出现次数超过一半的数字
30找出最小的K个数
31连续子数组的最大和
32从1到整数n中1出现的次数
33把数组中的数排成一个最小的数
34求第N个丑数
35第一个出现一次的字符
36数组中逆序对的个数
37两个链表的第一个公共节点
38数字在排序数组中出现的次数
39二叉树的深度
40数组中只出现一次的两个数,而其他数都出现两次。
41和为s的连续整数序列
42翻转字符串
43n个骰子的点数及出现的概率
44扑克牌的顺子
45圆圈中最后剩下的数
46 1+2+3+…+n的和
47不用加减乘除做加法
48不能被继承的类
49字符串转换为整数
50树中两个节点的最低公共祖先
51找出重复的数
52构建乘积数组
53正则表达式匹配
54表示数值的字符串
55字符流中第一个不重复的字符
56链表中环的入口节点
57删除链表中重复的节点
58二叉树的下一个节点
59对称的二叉树
60按之字形顺序打印二叉树
61把二叉树打印成多行
62序列化二叉树
63二叉搜索树的第K个节点
64数据流中的中位数
65滑动窗口的最大值
66矩阵中的路径
67机器人的运动范围
注:加粗字体的题目是热门面试题。
三、代码实现
1-10题
11-20题
21-30题
31-40题
41-50题
51-67题
GitHub地址:https://github.com/GaoLeiQin/SwordOffer
四、PDF下载
免费下载网址:《剑指Offer》题目、思路及Java版代码(带目录修订版)
————————————————
版权声明:本文为CSDN博主「白夜行515」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/baiye_xing/article/details/78428561
2 单例
https://blog.csdn.net/kris1025/article/details/104657059?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159399348919195239805004%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=159399348919195239805004&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v1~rank_blog_v1-3-104657059.pc_v1_rank_blog_v1&utm_term=%E5%8D%95%E4%BE%8B
静态内部类实现单例
public class SingletonPattern {
private SingletonPattern() {
}
private static class SingletonPatternHolder {
private static final SingletonPattern singletonPattern = new SingletonPattern();
}
public static SingletonPattern getInstance() {
return SingletonPatternHolder.singletonPattern;
}
}
3 二维数组中查找目标值
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:从右上角或左下角开始找,逐行删除,或者用二分法查找
public boolean find(int[][] array,int target) {
if (array == null) {
return false;
}
int row = 0;
int column = array[0].length-1;
while (row < array.length && column >= 0) {
if(array[row][column] == target) {
return true;
}
if(array[row][column] > target) {
column--;
} else {
row++;
}
}
return false;
}
9.1 斐波拉齐数列第n 项
9.2.一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路 :9.2和9.1 解法相同 使用非递归的解法(动态规划),效率上来说也会比递归好很多,不仅仅是因为递归会使得栈的深度变得很大,而且在递归的过程中,求解 getNums(n - 1) + getNums(n - 2) 时,在不同的 n 值下,还会产生很多重复的计算。而非递归的方式仅仅只是获取到之前的值使用,不存在重复的计算消耗。
// 1 递归
public static int getFibonacci(int n) {
if (n <= 1) return n;
return getNums(n - 1) + getNums(n - 2);
}
// 2 动态规划 循环
public static int getFibonacci(int n) {
if (n <= 1) return n;
// 使用一个长度为 2 的数组存储前两位的值
int[] nums = new int[] { 0, 1 };
int result = 0;
for (int i = 2; i <= n; i++) {
result = nums[0] + nums[1];
nums[0] = nums[1];
nums[1] = result;
}
return result;
}
//
public long fibonacci(int n) {
long result=0;
long preOne=1;
long preTwo=0;
if(n==0) {
return preTwo;
}
if(n==1) {
return preOne;
}
for (int i = 2; i <= n; i++) {
result = preOne+preTwo;
preTwo = preOne;
preOne = result;
}
return result;
}
9.3 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
思路:斐波那契数列思想
public int Fibonaccik(int n) {
int number = 1;
int sum = 1;
if (n <= 0)
return 0;
if (n == 1 ) {
return 1;
}
while (n-- >= 2) {
sum += number;
number = sum - number;
}
return sum;
}
9.4一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路:2^(n-1)
代码实现:
public int JumpFloor2(int target) {
return (int) Math.pow(2,target-1);
}
14使数组中的奇数位于偶数前面
// 解法一:移动偶数位置,时间复杂度 O(n²),空间复杂度 O(1)
public void reOrderArray(int[] array) {
if (array == null || array.length == 0) {
return ;
}
for (int i = 1; i < array.length; i++) {
int j = i - 1;
if (array[i] % 2 != 0) {
while (j >= 0) {
if (array[j] % 2 != 0) {
break;
}
if (array[j] % 2 == 0) {
int t = array[j + 1];
array[j + 1] = array[j];
array[j] = t;
j--;
}
}
}
}
}
// 解法二:双指针法,时间复杂度 O(n),空间复杂度 O(1)
public void reOrderArray(int[] array) {
if (array == null || array.length == 0) {
return;
}
int left = 0;
int right = array.length - 1;
while (left < right) {
while (left < right && array[left] % 2 != 0) {
left++;
}
while (left < right && array[right] % 2 == 0) {
right--;
}
if (left < right) {
int tmp = array[left];
array[left] = array[right];
array[right] = tmp;
}
}
}
子数组最大平均值
第一种方法():外循环固定子数组的开头,内循环用来求子数组的平均值。
缺点:双重循环,时间复杂度为O(n*n)
第二种方法:用类似用计算机网络中的滑动窗口协议的思想。窗口起初覆盖k个数,窗口每向后移动一个,前面就丢弃一个。
class Solution {
public:
/**
* @param nums: an array
* @param k: an integer
* @return: the maximum average value
*/
double findMaxAverage(int[] nums, int k) {
// Write your code here
int sum=0;
int len = nums.size();
for (int i = 0; i < k; i++) {
/* code */
sum+=nums[i];
}
int max=sum;
for (int i = k; i < len; i++) {
/* code */
sum-=nums[i-k];
sum+=nums[i];
max=Math.max(sum,Max);
}
return (double)max/k;
}
};
35. 在字符串中找出第一个只出现一次的字符。如输入"abaccdeff",则输出’b’。
public static Character firstNotRepeating(String str){
if(str == null)
return null;
char[] strChar = str.toCharArray();
LinkedHashMap hash = new LinkedHashMap();
for(char item:strChar){
if(hash.containsKey(item))
hash.put(item, hash.get(item)+1);
else
hash.put(item, 1);
}
for(char key:hash.keySet())
{
if(hash.get(key)== 1)
return key;
}
return null;
}
LeetCode 215. 数组中的第K个最大元素
快排 :时间复杂度 O(NlogN),空间复杂度 O(1)
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length - k];
}
冒泡排序:
public int findKthLargest(int[] nums, int k) {
int s = 0;
for(int i=0; i nums[j]){
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
}
if(s++ >= k) break;
}
return nums[nums.length-k];
}
堆排序 :时间复杂度 O(NlogK),空间复杂度 O(K)。
————————————————
版权声明:本文为CSDN博主「叶子在这儿」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ccccc1997/article/details/81673753
https://blog.csdn.net/qq_34538534/article/details/93377862
5 从尾到头打印链表
思路:借助栈实现,或使用递归的方法。
public ArrayList printListFromTailToHead(ListNode listNode) {
ArrayList list = new ArrayList<>();
if (listNode == null)
return list;
Stack stack = new Stack<>();
while (listNode != null) {
stack.push(listNode);
listNode = listNode.next;
}
while (!stack.isEmpty()) {
list.add(stack.pop().val);
}
return list;
}
13.O(1)时间删除链表节点
思路:将要删除节点的下一个节点的值赋给要删除的节点,然后指向下下一个节点
public void deleteNode(ListNode head, ListNode deListNode) {
if (deListNode == null || head == null)
return;
if (head == deListNode) {
head = null;
} else {
// 若删除节点是末尾节点,往后移一个
if (deListNode.nextNode == null) {
ListNode pointListNode = head;
while (pointListNode.nextNode.nextNode != null) {
pointListNode = pointListNode.nextNode;
}
pointListNode.nextNode = null;
} else {
deListNode.data = deListNode.nextNode.data;
deListNode.nextNode = deListNode.nextNode.nextNode;
}
}
}
15 查找链表倒数第k个节点
扩展题:找中间节点,使用两个指针,一个走一步,一个走两步。找到中间节点
思路:定义一快一慢两个指针,快指针走K步,然后慢指针开始走,快指针到尾时,慢指针就找到了倒数第K个节点。
代码实现:
public ListNode FindKthToTail(ListNode head,int k) {
if (head == null || k <= 0) {
return null;
}
ListNode fast = head;
ListNode slow = head;
while(k-- > 1) {
if (fast.next != null)
fast = fast.next;
else
return null;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
16 输出反转后的链表
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//首先判断链表是否为空,为空的话则返回空
if(head==null)
{
return null;
}
//pre表示head的前一个节点
ListNode pre=null;
//next表示head的后一个节点
ListNode next=null;
//一直循环到head为空
while(head!=null)
{
//为了防止链表断裂,先储存后一个节点
next=head.next;
//让head的下一个节点指向前一个节点,也就是实现了反转
head.next=pre;
//因为要继续向下再一次循环执行,所以移位,可看附图进行理解
pre=head;
head=next;
}
//当head为空的时候,pre已经是最后一个元素了,也就是现在链表的表头
return pre;
}
}
17 合并两个有序链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
else if (l2 == null) {
return l1;
}
else if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
37 查找2个链表的第一个公共节点
思路 :
1 hash map
2首先遍历两个链表得到他们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个结点。在第二次遍历的时候,在较长的链表上先走若干步,接着同时在两个链表上遍历,找到的第一个相同的结点就是他们的第一个公共结点。
//解法一:运用HasnMap的特性
import java.util.HashMap;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode current1 = pHead1;
ListNode current2 = pHead2;
HashMap hashMap = new HashMap();
while (current1 != null) {
hashMap.put(current1, null);
current1 = current1.next;
}
while (current2 != null) {
if (hashMap.containsKey(current2))
return current2;
current2 = current2.next;
}
return null;
}
}
// 方法2
public class Solution {
public static void main(String[] args) {
//构造链表结构测试用
ListNode a = new ListNode(1);
ListNode b = new ListNode(2);
ListNode c = new ListNode(3);
ListNode d = new ListNode(4);
ListNode e = new ListNode(5);
ListNode f = new ListNode(6);
ListNode g = new ListNode(7);
//第一个List
a.next = b;
b.next = c;
c.next = f;
f.next = g;
//第二个List
d.next = e;
e.next = f;
f.next = g;
Long begintime = System.nanoTime();
ListNode result = FindFirstCommonNode(a,d);
Long endtime = System.nanoTime();
if(result!=null){
System.out.println("两个链表的第一个公共节点为:"+result.val+",运行时间:"+(endtime - begintime) + "ns");
}else{
System.out.println("两个链表不相交");
}
}
public static ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
int count1 = 0;
int count2 = 0;
ListNode commonNode = null;
ListNode pNode1 = pHead1;
ListNode pNode2 = pHead2;
//得到链表1的长度
while(pNode1!=null){
count1++;
pNode1 = pNode1.next;
}
//得到链表2的长度
System.out.println("List1的长度为:"+count1);
while(pNode2!=null){
count2++;
pNode2 = pNode2.next;
}
System.out.println("List1的长度为:"+count2);
//令pNode1和pNode2重新指向头结点
pNode1 = pHead1;
pNode2 = pHead2;
int sub = count1 - count2;
System.out.println("两个List相差"+sub+"个节点");
//先在长链表上走几步,再同时在两个链表上遍历
if(sub>0){
for(int i = 0;i < sub;i++){
pNode1 = pNode1.next;
}
}else{
for(int i = 0;i < Math.abs(sub);i++){
pNode2 = pNode2.next;
}
}
System.out.println("List1从"+pNode1.val+"开始比较,List2从"+pNode2.val+"开始比较");
//得到第一个公共节点
while(pNode1!=null && pNode2!=null){
if(pNode1!=pNode2){
pNode1 = pNode1.next;
pNode2 = pNode2.next;
}else{
commonNode = pNode1;
return commonNode;
}
}
return commonNode;
}
}
56链表中环的入口节点
思路 : hash表实现
public ListNode detectCycle(ListNode head) {
Set visited = new HashSet();
ListNode node = head;
while (node != null) {
if (visited.contains(node)) {
return node;
}
visited.add(node);
node = node.next;
}
return null;
}
7.用两个栈来实现一个队列,完成队列的Push和Pop操作。
思路:一个栈压入元素,而另一个栈作为缓冲,将栈1的元素出栈后压入栈2中
public class stack2List {
private Stack stack1 = new Stack();
private Stack stack2 = new Stack();
public void push(String node) {
stack1.push(node);
}
public String pop() throws Exception {
if (stack1.isEmpty() && stack2.isEmpty()) {
throw new Exception("栈为空!");
}
if (stack2.isEmpty()) {
while(!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
6 由前序和中序遍历重建二叉树
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路: 先找出根节点,然后利用递归方法构造二叉树
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
//由于重建二叉树的过程会用到很多边界值,所以题目所给的方法的参数是不够用的
//所以,在下面重载了这个方法,每次传入前序和中序序列以及起始位置
TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
return root;
}
private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
//这里是判断结束的标志,由于是递归算法,我们不可能一直执行下去,所以需要结束标志
//下面这两种情况发生一个就会结束
if(startPre>endPre||startIn>endIn)
{
return null;
}
//首先找到根节点
TreeNode root=new TreeNode(pre[startPre]);
//对中序遍历进行查找根节点
for(int i=startIn;i<=endIn;i++)
{
//找到之后,分别对左子树和右子树进行递归算法,重复此步骤
if(in[i]==pre[startPre])
{
//重建二叉树的关键就是找到其中的边界值,边界值在图中已经做了描述
root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
root.right=reConstructBinaryTree(pre,startPre+i-startIn+1,endPre,in,i+1,endIn);
break;
}
}
return root;
}
}
26 后序遍历二叉搜索树
思路: 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
判断一个数组是否是二叉查找树的后序遍历的结果首先要知道二叉查找树的后序遍历是怎么样的。
二叉查找树的特点:在除了叶子节点之外的其它节点中,节点的左子树的任意一个数不大于这个节点,右子树的任意一个数不小于这个节点。
二叉树的后序遍历:对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。
递归判断T是不是都是二叉树的后序遍历。
public class Solution {
public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence == null || sequence.length == 0) {
return false;
}
return verifySquenceOfBST(sequence, 0, sequence.length - 1);
}
boolean verifySquenceOfBST(int[] sequence, int start, int end) {
if (start == end) {
return true;
}
int key = sequence[end];
int i = start;
for (; i < end; i++) {
if (sequence[i] > key) {//找到比最后一个元素大的第一个元素,确认这个T的左子树和右子树
break;
}
}
int m = i;
for (; i < end; i++) {
if (sequence[i] < key) {//如果右子树中是否有比最后一个元素小的元素,返回false
return false;
}
}
if (m == start || m == end) {//所有元素都比最后一个元素小的情况,即这个序列T只有左子树,此时继续判断它的左子树。 第一个元素比最后一个元素大时,同理,
//继续判断它的右子树
return verifySquenceOfBST(sequence, start, end - 1);
} else {
return verifySquenceOfBST(sequence, start, m - 1) && verifySquenceOfBST(sequence, m, end - 1);
}
}
}
63 二叉搜索树的第k个节点
思路:中序遍历二叉搜索树, 找到第k个节点
二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序,第k个结点就是第K大的节点,分别递归查找左右子树的第K个节点,或使用非递归借用栈的方式查找,当count=k时返回根节点。
1 递归实现
2 栈实现
1 栈实现
TreeNode KthNode(BinaryTreeNode root, int k){
if(root==null||k==0) return null;
int count=0;
Stack stack=new Stack<>();
while(root!=null||!stack.isEmpty()){
while (root != null) {
stack.push(root);
root = root.left;
}
root=stack.pop();
count++;
if(count==k) return root;
root=root.right;
}
return null;
}
2 递归实现
static int index=0;
static BinaryTreeNode KthNode(BinaryTreeNode pRoot,int k) {
if(pRoot==null || k<=0)
return null;
return getKthNode(pRoot,k);
}
private static BinaryTreeNode getKthNode(BinaryTreeNode pRoot,int k) {
BinaryTreeNode KthNode = null;
if(pRoot.left != null)//如果该节点有左孩子,就一直递归到左叶子节点
KthNode = getKthNode(pRoot.left,k);
if(KthNode==null) {
index++; //中序遍历的计数
if(k==index)
KthNode = pRoot;
}
if(KthNode==null && pRoot.right!=null)
//如果该节点有右孩子,就递归到右孩子
KthNode = getKthNode(pRoot.right,k);
return KthNode;
}
}
18 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路 输入两颗二叉树A,B,判断B是不是A的子结构。
解题思路:
1、找到A中和B的根节点相同的节点,然后进行判断是否相同。
2、如果不同再拿A的左子树与B进行比较。
3、如果仍不同再拿A的右子树与B进行比较。
4、如果仍未找到,则A中不包含B。
判断两个根节点相同的两个树是否包含:
1、先判断B,如果B为空说明包含。
2、再判断A,如果A为空说明不包含。
3、如果A的值与B的值相同然后继续进行此判断。
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
boolean result = false;
if (root2 != null && root1 != null) {
if(root1.val == root2.val){
result = doesTree1HaveTree2(root1,root2);
}
if (!result)
return HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2);
}
return result;
}
public boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
if (node2 == null) {
return true;
}
if (node1 == null) {
return false;
}
if (node1.val != node2.val) {
return false;
}
return doesTree1HaveTree2(node1.left,node2.left) &&
doesTree1HaveTree2(node1.right,node2.right);
}
19 二叉树的镜像
思路:
首先理解什么叫二叉树的镜像。我们都知道镜子具有左右相反的功能。故二叉树的镜像树是除根结点以外,其他结点都有顺序的左右交换了顺序。比如上面的图中所示:根结点8没有变换位置,可以根结点的左右结点发生了位置交换。接着以6、10为根结点子树的叶子结点也交换了位置。故我们求一颗镜像树的过程为:我们先前序遍历这颗树的每个结点,如果遍历到的结点有子结点,我们就交换它的两个子结点。当交换完所有非叶子结点的左右子结点之后,我们就得到了一颗树的镜像树。
————————————————
版权声明:本文为CSDN博主「不贰过先生」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013132035/article/details/80593217
//1 递归
public void mirrorRecursively(BinaryTreeNode pNode){
if(pNode == null){ //没有树直接返回
return;
}
if(pNode.left == null && pNode.right == null){ //如果左右结点为空,也直接返回
return;
}
BinaryTreeNode pTemp = pNode.left; //交换树的左右结点
pNode.left = pNode.right;
pNode.right = pTemp;
if(pNode.left!=null){ //如果左结点不为空,继续
mirrorRecursively(pNode.left);
}
if(pNode.right != null){ //如果右结点不为空,继续
mirrorRecursively(pNode.right);
}
}
2 // 栈循环实现
public void mirrorRecursively1(BinaryTreeNode pNode){
if(pNode == null){
return ;
}
Stack stack = new Stack();
stack.push(pNode);
while(!stack.isEmpty()){
BinaryTreeNode treeNode = stack.pop();
if(treeNode.left!=null || treeNode.right!=null){
BinaryTreeNode tempNode = treeNode.left;
treeNode.left = treeNode.right;
treeNode.right = tempNode;
}
if(treeNode.left!=null){
stack.push(treeNode.left);
}
if(treeNode.right!=null){
stack.push(treeNode.right);
}
}
}
** 25 二叉树和为某一值的所有路径的**
**思路 **
当用前序遍历的方式访问到某一结点时,我们把该结点添加到路径上,并累加该结点的值。如果该结点为叶结点并且路径中结点值的和刚好等于输入的整数,则当前的路径符合要求,我们把它打印出来。如果当前结点不是叶结点,则继续访问它的子结点。当前结点访问结束后,递归函数将自动回到它的父结点。因此我们在函数退出之前要在路径上删除当前结点并减去当前结点的值,以确保返回父结点时路径刚好是从根节点到父结点的路径。从分析中得知我们需要借助栈来保存路径的数据。
public ArrayList> findPath(BinaryTreeNode pRoot, int expectedSum){
ArrayList> pathList = new ArrayList>();
if(pRoot == null){ //如果此树为空则直接返回
return null;
}
Stack path = new Stack();//定义栈来存储一条路径
findPath(pRoot, expectedSum, path, pathList); //调用查找方法
return pathList;
}
private void findPath(BinaryTreeNode pRoot, int expectedSum,
Stack path, ArrayList> pathList) {
if(pRoot == null){
return;
}
//判断其是不是叶子结点,如果是看其是不是等于期望值,是的话直接添加到ArrayList集合当中
if(pRoot.leftNode == null && pRoot.rightNode == null){
if(pRoot.value == expectedSum){ //如果等于期望值,则添加到list集合中
ArrayList list = new ArrayList();
for (int value : path) { //将路径上的值添加集合中
list.add(value);
}
list.add(pRoot.value);//将根节点值添加入集合
pathList.add(list); //将这条路径添加到pathList集合中
}
}else{
//不是叶子结点,前序遍历,将当前结点值放入path栈中
path.push(pRoot.value);
findPath(pRoot.leftNode, expectedSum - pRoot.value, path, pathList);
findPath(pRoot.rightNode, expectedSum - pRoot.value, path, pathList);
//在返回到父结点之前,在路径上删除当前结点的值
path.pop();
}
}
方法一: AtomicInteger countDownLatch 实现 推荐
public class TestThread {
private static AtomicInteger num = new AtomicInteger(1);
private static CountDownLatch countDownLatch = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (num.intValue() < 100) {
if (num.intValue() % 2 == 1) {
System.out.println("奇数线程:"+num.intValue());
num.incrementAndGet();
}
countDownLatch.countDown();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
while (num.intValue() <= 100) {
if (num.intValue() % 2 == 0) {
System.out.println("偶数线程:"+num.intValue());
num.incrementAndGet();
}
countDownLatch.countDown();
}
}
};
t1.start();
t2.start();
countDownLatch.await();
}
}
方法二: synchronized notify watie 实现
public class TestThread {
public static int i = 1;
public static final int TOTAL = 100;
public static Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (i <= TOTAL) {
synchronized (lock) {
if (i % 2 == 1) {
System.out.println("i=" + i++);
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
Thread thread2 = new Thread(() -> {
while (i <= TOTAL) {
synchronized (lock) {
if (i % 2 == 0) {
System.out.println("i=" + i++);
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
thread1.start();
thread2.start();
}
}