只能说受益匪浅
1
判定入栈,出栈序列是否匹配
// 思路:用辅助栈来模拟出入栈
import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
int cnt = 0; // 记录出栈个数或下标
Stack<Integer> stack = new Stack<>(); // 辅助栈
for(int i = 0; i < pushA.length; i++){
stack.push(pushA[i]); // 模拟入栈
while(!stack.isEmpty() && stack.peek() == popA[cnt]){ // while循环模拟出栈
stack.pop();
cnt++;
}
}
return stack.isEmpty(); // 判断辅助栈是否为空
}
}
2
从上往下层级遍历二叉树
// 思路:用一个 队列 模拟层次
// 用LinkedList模拟队列
// 栈是addFirst,removeFirst
// 队列addLast,removeFirst
// 这里的层次遍历,不是一层层来的,是一层里面分左右子树分开来的
import java.util.ArrayList;
import java.util.LinkedList;
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list = new ArrayList(); // 存取层次遍历序列
LinkedList<TreeNode> queue = new LinkedList(); // 存储节点模拟层次的
if(root == null) return list;
queue.addLast(root);
while(!queue.isEmpty()){
TreeNode temp = queue.removeFirst(); // 出队
list.add(temp.val);
if(temp.left != null){
queue.addLast(temp.left);
}
if(temp.right != null){
queue.addLast(temp.right);
}
}
return list;
}
}
// 用ArrayList模拟队列
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list = new ArrayList();
ArrayList<TreeNode> queue = new ArrayList();
if(root == null) return list;
queue.add(root);
while(queue.size() != 0){
TreeNode temp = queue.remove(0);
list.add(temp.val);
if(temp.left != null){
queue.add(temp.left);
}
if(temp.right != null){
queue.add(temp.right);
}
}
return list;
}
}
3
判断是否后序遍历
// 现在开始自己规定,凡是自己传进去的数组长度这些参数,都是实际长度,就是length-1这种
// 思路:后序中最后一个是根,去除最后一个可以分成两段。前段小于根,后段大于根,以此类推递归
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence == null || sequence.length == 0) return false;
return search(sequence,0,sequence.length-1);
}
private boolean search(int[] arr,int left,int right){
if(left >= right) return true; // 递归出口,这里最重要
int mid = left; // 从左遍历找分界(对比根),小心越界
while(arr[mid] < arr[right] && mid < right){
mid++;
}
for(int i = mid; i < right; i++){ // 判断右端是否符合
if(arr[i] < arr[right]){
return false;
}
}
// 左去界(遍历的时候比根大了才停止的),右去根
return search(arr,left,mid-1) && search(arr,mid,right-1);
}
}
4
二叉树和为某值的路径
import java.util.ArrayList;
public class Solution {
// 一个保存当前遍历的路径,一个保存符合的全部路径
ArrayList<ArrayList<Integer>> list = new ArrayList<>();
ArrayList<Integer> path = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
search(root,target);
return list;
}
// 遍历用前序或者DFS
private void search(TreeNode root, int target){
if(root == null) return ;
target -= root.val;
path.add(root.val);
if(root.left == null && root.right == null && target == 0){
list.add(new ArrayList<Integer>(path));
}
search(root.left,target);
search(root.right,target);
// 回溯时不用加回target,因为是个副本
path.remove(path.size()-1);
}
}
5
复杂链表的复制
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
// 思路:
// 1. 复制每个节点(暂不处理随机指向),将新复制的节点插入原节点后面:A->A1
// 2. 处理随机指向
// 3. 复制链表和原链表分离
public class Solution {
public RandomListNode Clone(RandomListNode pHead){
if(pHead == null) return null;
// 1. 复制链表,复制节点插入到原节点后面
RandomListNode node = pHead;
while(node != null){
RandomListNode next = node.next;
RandomListNode cloneNode = new RandomListNode(node.label);
node.next = cloneNode; // 链表插入过程
cloneNode.next = next;
node = next; // 节点插入后,当前节点记得跳转到next
}
// 2. 遍历处理随机指向
node = pHead;
while(node != null){
if(node.random != null){
// 重点:指向随机的下一个(因复制时插入到后一个去了)
node.next.random = node.random.next;
}
node = node.next.next; // 复制插入要跳多一个
}
// 3. 分离节点,奇偶分离
RandomListNode oldNode = pHead;
RandomListNode newHead = pHead.next; // 新表头
while(oldNode != null){
RandomListNode newNode = oldNode.next;
oldNode.next = newNode.next;
if(newNode.next != null){
newNode.next = newNode.next.next;
}
oldNode = oldNode.next; // 上面已经更新了旧节点指向,已经跳过一个节点了
}
return newHead;
}
}
// 3. 分离节点,奇偶分离
// RandomListNode oldNode = pHead;
// RandomListNode newNode = pHead.next; // 因为有复制,所以后一个节点一定不为空
// RandomListNode newHead = newNode; // 新表头
// while(newNode.next != null){
// oldNode.next = newNode.next;
// oldNode = oldNode.next;
// newNode.next = oldNode.next;
// newNode = newNode.next;
// }
6
二叉搜索树转变双向链表
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
/**
* 递归中序遍历:左 根 右
* 下面if、else中的意思
* 4
/
* 3 5
* 第一步if:head与temp赋值3节点;
* 第二步else:改动temp节点互相指向,最后head赋值4节点:3 <--> 4
* 第三步else:改动temp节点互相指向,最后head赋值5节点:4 <--> 5
* 综上:3 <--> 4 <--> 5,链表完成
*/
public class Solution {
TreeNode temp = null; // 临时节点,帮助形成双向链表
TreeNode head = null; // 表头,用于返回
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) return null; // 递归出口
Convert(pRootOfTree.left); // 左子树遍历
if (head == null) { // 首次要处理根节点
head = pRootOfTree; // 第一次访问,记录头节点,用于访问返回
temp = pRootOfTree;
} else {
temp.right = pRootOfTree; // 按中序遍历顺序连成链表,详情看上面图
pRootOfTree.left = temp; // 中序就是有序,只需将当前temp指向下一个即可
temp = temp.right; // 然后移动当前节点到下一个
}
Convert(pRootOfTree.right); // 右子树递归
return head;
}
}
7
字符串的排列(标准的DFS + 交换 / 回溯)
// 思路:根据字符串的排列的特点,选择深度优先搜索,可通过字符交换实现,重复字符用剪枝
// 1. 分成两部分,首个字符、后面的全部字符
// 2. 每个字符都可在首位,即首字符和后面的进行交换
// 3. 固定第一个字符,求后面的排列,即递归进行2,3步,出口为到了数组长度
import java.util.HashSet;
import java.util.ArrayList;
import java.util.Collections;
public class Solution {
// 保存所有排列
ArrayList<String> res = new ArrayList();
public ArrayList<String> Permutation(String str) {
char[] arr = str.toCharArray(); // 转成数组容易遍历
dfs(arr,0); // 实现排列
Collections.sort(res); // 排序
return (ArrayList) res;
}
private void dfs(char[] arr,int index){
if(index == arr.length - 1){ // 到叶子节点认为一个排列,递归出口
res.add(String.valueOf(arr));
return ;
}
HashSet<Character> set = new HashSet<>(); // 用来做剪枝的,防止重复
for(int i = index; i < arr.length; i++){ // 遍历交换:使得每个元素都在首位
if(set.contains(arr[i])){
continue; // 重复,因此剪枝(假设每个元素不重复)
}
set.add(arr[i]);
swap(arr,index,i); // 1. 首位和后面的全部逐个交换,即每个元素都有在首位的可能
dfs(arr,index + 1); // 2. 固定首位,排列后面的字符
swap(arr,index,i); // 3. 回溯,不影响后面的每个元素都排在首位
}
}
// 交换
private void swap(char[] arr,int i,int j){
char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
8
找出数组中的一个出现的次数超过数组长度的一半的数
// 思路1:遍历多次,保存每个元素出现的次数
// 思路2:排序后,众数肯定出现在中间
// 最优解
// 思路:摩尔投票法,查找超过1/2的数,肯定只有一个
// 流程:依次从序列中选择两个数字,若不同则抵消(票数-1),相同当前数值的票数+1,最后剩下的数字就是所找
// 步骤:1.对抗两两抵消、2.计算结果是否有效
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int major = 0;
int count = 0; // 当前major的票数
for(int i = 0; i < array.length; i++){ // 从头到尾遍历
if(count == 0){
major = array[i];
}
if(major == array[i]){
count++;
}else{
count--;
}
}
int countRs = 0;
for(int num : array){
if(major == num){
countRs++;
}
}
return (countRs > array.length/2) ? major : 0;
}
}
9
找出其中最小的K个数,TopK问题(快排,堆排)
// 无脑解法
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
Arrays.sort(input);
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0; i < k; i++){
list.add(input[i]);
}
return list;
}
}
// 快排,我叫为哨兵排
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
if(input == null || k > input.length) return list;
quickSort(input,0,input.length-1);
ArrayList<Integer> list = new ArrayList();
for(int i = 0; i < k; i++){
list.add(input[i]);
}
return list;
}
private void quickSort(int[] arr, int left,int right){
if(left > right) return ;
int base = arr[left];
int i = left,j = right;
while(i < j){ // 选基准交换
while(i < j && base <= arr[j]){
j--;
}
while(i < j && base >= arr[i]){
i++;
}
if(i < j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
arr[left] = arr[i]; // 基准归位
arr[i] = base;
quickSort(arr,left,i-1); // 二分治
quickSort(arr,i+1,right);
}
}
10
计算连续子向量的最大和,有负数
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if(array == null) return 0;
// 注意:题目指的连续,不一定从下标0开始,可以窗口滑动的
// maxSum不初始化为0,存在全负数情况,所以初始值array[0]
// maxSum存储最大和
int curSum,maxSum;
curSum = maxSum = array[0];
for(int i = 1; i < array.length; i++){
// 一旦遇到和为负数,证明前面的正数效果作废了
// 当前和小于0,抛弃前面的和,重新从现在加起
if(curSum < 0){
curSum = array[i];
}else if(curSum > 0){
curSum += array[i];
}
// 更新最大和
if(curSum > maxSum){
maxSum = curSum;
}
}
return maxSum;
}
}
11
计算整数中1出现的次数
// 暴力转成字符串判断
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
StringBuffer str = new StringBuffer();
for(int i = 1;i <= n; i++){
str.append(i);
}
int count = 0;
String s = str.toString();
for(int i = 0;i < s.length(); i++){
if(s.charAt(i) == '1'){
count++;
}
}
return count;
}
}
// 区分位数:i表示当前位,其余表示为高位和低位
// 每次循环取当前位,即高位模10(high % 10),分别有三种情况。如下:
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count = 0;
for(int i = 1; i <= n; i *= 10){ // 只需循环位数次
int high = n / i;
int low = n % i;
if(high % 10 == 0){
count += high / 10 * i;
}else if (high % 10 == 1){
count += (high / 10 * i) + (low + 1);
}else {
count += (high / 10 + 1) * i;
}
}
return count;
}
}
// 最优解,上面的优化
// 当百位 = 0,则high / 10 == (high + 8) / 10
// 当百位 > 1,取8就进位,效果等于(high / 10 + 1)
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int count = 0;
for(int i = 1; i <= n; i *=10){
int high = n / i;
int low = n % i;
if(high % 10 == 1){
count += low + 1;
}
count += (high + 8) / 10 * i;
}
return count;
}
}
12
把数组排成最小
// 本质是排序问题,一般用快排
public class Solution {
public String PrintMinNumber(int [] numbers) {
for(int i = 0; i < numbers.length-1; i++) // 冒泡排序
for(int j = 0; j < numbers.length-i-1; j++){
String str1 = numbers[j] + "" + numbers[j+1];
String str2 = numbers[j+1] + "" + numbers[j];
if(str1.compareTo(str2) > 0){ // 排到最后的是最大
int temp = numbers[j];
numbers[j] = numbers[j+1];
numbers[j+1] = temp;
}
}
String str = "";
for(int i = 0; i < numbers.length; i++){
str += numbers[i];
}
return str;
}
}
// 思路二
// 数字m、n拼接成 mn 和 nm
// 若mn>nm,则m大于n
// 若mn<nm,则m小于n
// 若mn=nm,则m等于n
13
丑数:把只包含质因子2、3和5的数
// 思路:一个丑数一定由另一个丑数乘以2或3或5得到
// 这就是动态规划??
import java.util.ArrayList;
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index <= 0) return 0;
ArrayList<Integer> list = new ArrayList();
list.add(1); // 默认第一个丑数为1
// 用三个下标来模拟三个队列的尾部,加入list证明已经排好序
int i2 = 0,i3 = 0,i5 = 0;
while(list.size() < index){ // 从各自的队列取出
int m2 = list.get(i2)*2;
int m3 = list.get(i3)*3;
int m5 = list.get(i5)*5;
int min = Math.min(m2,Math.min(m3,m5));
list.add(min);
if(min == m2) i2++;
if(min == m3) i3++;
if(min == m5) i5++;
}
return list.get(list.size()-1);
}
}
14
第一个只出现一次的字符位置
// 哈希表,VALUE存放次数
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if(str.length() == 0) return -1;
HashMap<Character,Integer> map = new HashMap();
char[] arr = str.toCharArray();
for(int i = 0; i < str.length(); i++){
if( map.containsKey(str.charAt(i)) ){
int num = map.get(str.charAt(i));
map.put(str.charAt(i),num+1);
}else{
map.put(str.charAt(i),1);
}
}
for(int i = 0; i < str.length(); i++){
if( map.get(str.charAt(i)) == 1 ){
return i;
}
}
return 0;
}
}
// 变形体,返回第一个只出现一次的字符
// 哈希表,VALUE存放次数,这里一次的话可以存放TRUE/FALSE
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
char[] arr = str.toCharArray();
HashMap<Character,Boolean> hashMap = new HashMap<>();
for(char c : arr){
hashMap.put(c,!hashMap.containsKey(c));
}
for(char c : arr){
if(hashMap.get(c)){
return c;
}
}
return "";
}
}
15
数组的逆序对
// 暴力破解法,双层for循环,内层以i+1开头(因为当前元素的前面才能构成逆序)
public class Solution {
public int reversePairs(int[] nums) {
int cnt = 0;
for (int i = 0; i < nums.length - 1; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] > nums[j]) {
cnt++;
}
}
}
return cnt;
}
}
// 最优解
// 归并排序的利用,分治过程中前后数字可对比,是统计的最佳时机
public class Solution {
int count = 0; // 统计逆序对
public int InversePairs(int [] array) {
if(array == null || array.length == 0) return 0;
mergeSort(array,0,array.length-1);
return count;
}
private void mergeSort(int[] arr,int start,int end){
if(start < end){ // 拆分分治的过程,递归出口,长度为1默认排好序
int mid = start + (end - start) / 2;
mergeSort(arr,start,mid);
mergeSort(arr,mid+1,end);
merge(arr,start,mid,end); // 最后合并
}
}
private void merge(int[] arr,int start,int mid,int end){
int[] temp = new int[end - start + 1]; // 辅助数组,最后赋值回原数组
int i = start,j = mid + 1;
int index = 0;
while(i <= mid && j <= end){
if(arr[i] > arr[j]){
temp[index++] = arr[j++];
// 与归并排序就多了下面这两句
// 合并数组时,array[i]大于后面array[j]时
// 则array[i]~array[mid]都是大于array[j]的,所以count += mid + 1 - i
count += mid - i + 1;
count = count > 1000000007 ? count % 1000000007 : count;
}else{
temp[index++] = arr[i++];
}
}
while(i <= mid)
temp[index++] = arr[i++];
while(j <= end)
temp[index++] = arr[j++];
for (int k = 0;k < temp.length;k++)
arr[start+k] = temp[k];
}
}
16
两个链表的第一个公共结点
// 先走链表二者长度差,然后同步走到相同节点
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
// 0. 移动节点要记得复位,这里卡了好久,不然NPE
ListNode temp1 = pHead1;
ListNode temp2 = pHead2;
// 1. 记录二者的长度
int p1 = 0, p2 = 0;
while(pHead1 != null){
p1++;
pHead1 = pHead1.next;
}
while(pHead2 != null){
p2++;
pHead2 = pHead2.next;
}
if(pHead1 != pHead2) return null; // 尾节点都不相交,下面也无需遍历了,简化操作可忽略
// 2. 上面移动指针要复位
// 移动长链表,移动距离为二者长度差
pHead1 = temp1;
pHead2 = temp2;
if(p1 > p2){
int temp = p1 - p2;
while(temp > 0){
pHead1 = pHead1.next;
temp--;
}
}else{
int temp = p2 - p1;
while(temp > 0){
pHead2 = pHead2.next;
temp--;
}
}
// 3. 二者并行找相同节点
while(pHead1 != null || pHead2 != null){
if(pHead1 == pHead2){
return pHead1;
}
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
// 4. 没有公共节点
return null;
}
}
// 思路二,两条y状的链表,从尾遍历到头,第一个不相同的就是交点,使用栈/递归实现
// 思路三:最优解,双指针
// 两个指针同步走,哪个到了链表尾,就设置为对方的头节点继续遍历,最后会相遇
// 长度相同有公共结点,第一次就遍历到;没有公共结点,走到尾部NULL相遇,返回NULL
// 长度不同有公共结点,第一遍差值就出来了,第二遍一起到公共结点;没有公共,一起到结尾NULL
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode p1 = pHead1;
ListNode p2 = pHead2;
while(p1 != p2){
p1 = (p1 == null ? pHead2 : p1.next);
p2 = (p2 == null ? pHead1 : p2.next);
}
return p1;
}
}
17
统计一个数字在排序数组中出现的次数(排序就二分)
// 思路:傻子做法
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int cnt = 0;
for(int i = 0; i < array.length; i++){
if(k == array[i]){
cnt++;
}
}
return cnt;
}
}
// 思路:首先二分法,找到之后向前向后找
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int cnt = 0;
int left = 0;
int right = array.length - 1;
int mid = -1;
while(left <= right){
mid = left + (right-left) / 2;
if(array[mid] == k){
cnt++;
break;
}else if(array[mid] < k){
left = mid + 1;
}else{
right = mid - 1;
}
}
if(mid == -1) return cnt; // 没找到相同的,先退出了
for(int i = mid+1; i < array.length; i++){
if(array[i] == k) cnt++;
else break;
}
for(int i = mid-1; i >= 0; i--){
if(array[i] == k) cnt++;
else break;
}
return cnt;
}
}
// 思路三:最优,二分左右边界,相减即可
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length == 0) return 0;
int first = getFirstK(array,k);
int last = getLastK(array,k);
if(first == -1 || last == -1) return 0;
else return last - first + 1;
}
private int getFirstK(int [] array, int k){
int low = 0;
int high = array.length - 1;
while(low <= high){
int mid = low + (high-low) / 2;
if(array[mid] == k){
high = mid - 1;
}else if(array[mid] > k){
high = mid - 1;
}else{
low = mid + 1;
}
}
if(low == array.length) return -1; // 这里最重要
return array[low] == k ? low : -1;
}
private int getLastK(int [] array, int k){
int low = 0;
int high = array.length - 1;
while(low <= high){
int mid = low + (high - low) / 2;
if(array[mid] == k){
low = mid + 1;
}else if(array[mid] > k){
high = mid - 1;
}else{
low = mid + 1;
}
}
if(high == -1) return -1;
return array[high] == k ? high : -1;
}
}
18
求树深
// 递归
public class Solution {
public int TreeDepth(TreeNode root) {
// 递归出口
if(root == null) return 0;
// 递归条件
return Math.max( TreeDepth(root.left)+1 , TreeDepth(root.right)+1 );
}
}
// 层次遍历解决,此层次和之前的不同
// 每层次遍历一遍,即深度+1
// 遍历完就深度也出来了
// 注意,遍历一次就要一层全部处理完,否则就不是树深+1了
import java.util.LinkedList;
public class Solution {
public int TreeDepth(TreeNode root) {
int cnt = 0; // 层数
LinkedList<TreeNode> queue = new LinkedList(); // 存储节点模拟层次的
if(root == null) return cnt;
queue.addLast(root);
while(!queue.isEmpty()){
int size = queue.size();
for(int i = 0; i < size; i++){ // for将当前层处理完
TreeNode temp = queue.removeFirst(); // 出队
if(temp.left != null) queue.addLast(temp.left);
if(temp.right != null) queue.addLast(temp.right);
}
cnt++;
}
return cnt;
}
}
19
验证平衡二叉树平衡(任何结点的两个子树的高度差小于等于1),可以结合17题
// 递归
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
// 空也是一个平衡树
if(root == null) return true;
return getDepth(root) != -1;
}
// 后序遍历算深度,每个节点只用算一次
private int getDepth(TreeNode node){
if(node == null) return 0;
// 左树的深度,+1动作放到最后,因为下面要判断-1
int left = getDepth(node.left);
if(left == -1) return -1;
// 右树的深度
int right = getDepth(node.right);
if(right == -1) return -1;
// 左右树深度比较,也就是递归
if(Math.abs(left - right) > 1) return -1;
// 当前
return Math.max(left,right) + 1;
}
}
20
数组中只出现一次的数字
// num1,num2分别为长度为1的数组。传出参数,将num1[0],num2[0]设置为返回结果即可,C语言题目垃圾
// 用HashSet去重特性
import java.util.HashSet;
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
HashSet<Integer> set = new HashSet();
// set.add,如果存在返回true,如为false则插入元素
for(int i = 0; i < array.length; i++){
if(set.contains(array[i])){
set.remove(array[i]);
}else{
set.add(array[i]);
}
}
Object[] temp = set.toArray();
num1[0] = (int) temp[0];
num2[0] = (int) temp[1];
}
}
// 最优解
// 使用数组的异或运算,相同为0,不同为1,任何数与0异或为本身
// 如果一个数字只出现一次,其余两次,那么全体异或过程中两两相同的就会抵消变为0,剩下的数和0异或得出本身
// eg:
int res = 0;
int[] nums = {1,1,2,3,4,5,3,4,5};
for(int value : nums){
res ^= value;
}
System.out.println(res); // 2
// ——————————————————————————————————————————————————————————————————————————————————————
// 如果出现了两次,那么就要进行分组异或
// 假如两个不同的数为 a、b,那么所有数字异或结果就是 a^b 的结果,记为x
// x转成二进制,其中的0和1表示a、b二进制中相同和不同的部分
// 若选二进制x中,不为0的位,按照该位分组,默认选不为0的最低位
// 流程:
// 1.对所有数字异或,得出x
// 2.在x中找不为0的最低位
// 3.根据这位对所有的数字进行分组
// 4.在每个组内进行异或操作,得到两个数字
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int ret = 0;
for(int num : array){
ret ^= num;
}
int div = 1;
while((div & ret) == 0){
div <<= 1; // div两倍关系,才能在二进制上逐步变成1,所以分组也只能是2,4,6来分
}
int a = 0;
int b = 0;
for(int num : array){
if ((div & num) != 0) {
a ^= num;
} else {
b ^= num;
}
}
num1[0] = a;
num2[0] = b;
}
}