找出数组中出现奇数次的数
使用异或来解决,非常巧妙。在二进制中,负数等于它的反码加一。
public class EvenOddTimes {/*** 数组中只有一种数字,出现奇数次,找出这个数字*/public static void printOddTimesNum1(int[] arr) {int xor = 0;for (int i = 0; i < arr.length; i++) {xor ^= arr[i]; // 所有数字异或结果}System.out.println("数组中唯一出现奇数次的值为:" + xor);}public static void printOddTimesNum2(int[] arr) {int xor = 0;for (int i = 0; i < arr.length; i++) {xor ^= arr[i]; // 所有数字异或结果}int rightOne = xor & (~xor + 1);int onlyOne = 0;for (int i = 0; i < arr.length; i++) {if ((arr[i] & rightOne) != 0) {onlyOne ^= arr[i];}}System.out.println("数组中出现奇数次的两个数分别是:" +onlyOne + "," + (xor ^ onlyOne));}public static void main(String[] args) {int a = 5;int b = 7;a = a ^ b; // 使用异或来交换两个数的值b = a ^ b;a = a ^ b;System.out.println("交换过后:" + a);System.out.println("交换过后:" + b);int[] arr1 = {1, 1, 1, 1, 2, 2, 3};printOddTimesNum1(arr1);int[] arr2 = {1, 1, 1, 1, 2, 2, 3, 5, 5, 5};printOddTimesNum2(arr2);}}
检验链表是否是回文结构
三种方法,用栈或者是修改链表再复原。
public static class Node {public int value;public Node next;public Node(int data) {this.value = data;}}// 检验一个链表是否是回文链表// 方法1:使用栈public static boolean isPalindrome1(Node head) {Stack<Node> stack = new Stack<Node>();Node cur = head;while (cur != null) {stack.push(cur); // 全部压入栈中cur = cur.next;}while (head != null) {if (head.value != stack.pop().value) {return false;}head = head.next;}return true;}// 方法2:用栈,只压入一半数据public static boolean isPalindrome2(Node head) {if (head == null || head.next == null) {return true;}Node cur = head;Node right = head.next;// 异步指针,不论链表总数是奇数或偶数,right到达的位置很巧妙while (cur.next != null && cur.next.next != null) {cur = cur.next.next;right = right.next; // right到达的位置很巧妙}Stack<Node> stack = new Stack<>();while (right != null) {stack.push(right); // 把右半边的数据都压入栈中right = right.next;}while (!stack.isEmpty()) {if (head.value != stack.pop().value) {return false;}head = head.next;}return true;}// 方法3:快慢指针,反向修改链表,逐一比较,最后再把链表恢复public static boolean isPalindrome3(Node head) {if (head == null || head.next == null) {return true;}Node n1 = head;Node n2 = head;while (n2.next != null && n2.next.next != null) {n1 = n1.next; // 偶数时,n1和n2到中间两个结点n2 = n2.next.next; // 奇数时,n1到中间结点,n2到末尾结点}n2 = n1.next; // n2 -> 重新赋值到有半边开头n1.next = null; // 断开链表,mid.next -> nullNode n3 = null;while (n2 != null) { // 逆转右半边链表n3 = n2.next; // n3 -> save next noden2.next = n1; // next of right node convertn1 = n2; // n1 moven2 = n3; // n2 move}n3 = n1; // n3 -> 保存原始链表的末尾结点n2 = head; // n2 -> left first nodeboolean res = true;while (n1 != null && n2 != null) { // 从两边开始对比if (n1.value != n2.value) {res = false;break;}n1 = n1.next; // left to midn2 = n2.next; // right to mid}// 恢复链表n1 = n3.next;n3.next = null;while (n1 != null) { // recover listn2 = n1.next;n1.next = n3;n3 = n1;n1 = n2;}return res;}
有环链表的相交结点
判断两个链表是否有环。
判断两个链表是否相交。
package problems;public class ListWithCircle {public static class Node {public int value;public Node next;public Node(int data) {this.value = data;}}public static Node getCircleNode(Node head1, Node head2) {if (head1 == null || head2 == null) {return null;}// 先判断两个链表是否有环Node circle1 = ringList(head1);Node circle2 = ringList(head2);// 都是无环链表,如果相交,返回相交结点,否则返回nullif (circle1 == null && circle2 == null) {return joinPoint(head1, head2);}// 都是有环链表// 三种情况:if (circle1 != null && circle2 != null) {return boothRing(head1, circle1, head2, circle2);}return null;}// 1. 判断链表是否有环// 有环返回第一个入环结点,否则返回nullpublic static Node ringList(Node head) {if (head == null || head.next == null || head.next.next == null) {return null;}Node slow = head.next;Node fast = head.next.next;// 快慢指针,如果有环,它们会在环中某个结点相遇,否则返回nullwhile (slow != fast) {if (fast.next == null || fast.next.next == null) {return null;}slow = slow.next;fast = fast.next.next;}// 有环,两个指针相遇,一个指针回到开头,两个指针一起走,刚好走到入环结点slow = head;while (slow != fast) {slow = slow.next;fast = fast.next;}return slow;}// 2. 判断两个无环链表是否相交// 如果相交,返回第一个相交结点public static Node joinPoint(Node head1, Node head2) {if (head1 == null || head2 == null) {return null;}Node cur1 = head1;Node cur2 = head2;int n = 0;while (cur1.next != null) {n++;cur1 = cur1.next;}while (cur2.next != null) {n--;cur2 = cur2.next;}// 如果两个末尾结点不相同,则这两个单链表不相交if (cur1 != cur2) {return null;}cur1 = head1;cur2 = head2;// 长的先走 n 步if (n > 0) {cur1 = head1; // 从末尾重定位到开头while (n != 0) { // 先走n步n--;cur1 = cur1.next;}} else {// 不能放到这里,如果走if语句,就不会走到这一步,那么cur2就没有从末尾重定位到开头。。。// cur2 = head2;n = Math.abs(n);while (n != 0) {n--;cur2 = cur2.next;}}while (cur1 != cur2) {cur1 = cur1.next;cur2 = cur2.next;}return cur1;}// 3. 寻找两个有环链表的相交结点public static Node boothRing(Node head1, Node loop1, Node head2, Node loop2) {Node cur1 = null;Node cur2 = null;if (loop1 == loop2) {cur1 = head1;cur2 = head2;int n = 0;while (cur1 != loop1) {n++;cur1 = cur1.next;}while (cur2 != loop2) {n--;cur2 = cur2.next;}cur1 = n > 0 ? head1 : head2;cur2 = cur1 == head1 ? head2 : head1;n = Math.abs(n);while (n != 0) {n--;cur1 = cur1.next;}while (cur1 != cur2) {cur1 = cur1.next;cur2 = cur2.next;}return cur1;} else {cur1 = loop1.next;while (cur1 != loop1) {if (cur1 == loop2) {return loop1;}cur1 = cur1.next;}return null;}}public static void main(String[] args) {// 1->2->3->4->5->6->7->nullNode head1 = new Node(1);head1.next = new Node(2);head1.next.next = new Node(3);head1.next.next.next = new Node(4);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(6);head1.next.next.next.next.next.next = new Node(7);// 0->9->8->6->7->nullNode head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next.next.next.next.next; // 8->6System.out.println(getCircleNode(head1, head2).value);// 1->2->3->4->5->6->7->4...head1 = new Node(1);head1.next = new Node(2);head1.next.next = new Node(3);head1.next.next.next = new Node(4);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(6);head1.next.next.next.next.next.next = new Node(7);head1.next.next.next.next.next.next = head1.next.next.next; // 7->4// 0->9->8->2...head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next; // 8->2System.out.println(getCircleNode(head1, head2).value);// 0->9->8->6->4->5->6..head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next.next.next.next.next; // 8->6System.out.println(getCircleNode(head1, head2).value);}}
遍历二叉树
对二叉树进行遍历,先序、中序、后序和宽度遍历。
非递归版本,用到栈结构和队列结构。
package problems;import java.util.Deque;import java.util.LinkedList;import java.util.Queue;/*** 遍历二叉树* 先序、中序、后序*/public class TraversalTree {public static class Node {private int value;private Node left;private Node right;public Node(int value) {this.value = value;}}// 层次遍历// 宽度遍历,每层从左到右顺序遍历public static void levelOrder(Node cur) {if (cur == null) {return;}System.out.print("宽度遍历:");Queue<Node> queue = new LinkedList<>(); // 队列queue.add(cur);while (!queue.isEmpty()){cur = queue.poll(); // 先进先出System.out.print(cur.value + ", ");if (cur.left != null) {queue.add(cur.left);}if (cur.right != null) {queue.add(cur.right);}}}// 先序遍历// 用栈结构,先压右,再压左public static void preOrder(Node cur) {System.out.print("先序遍历:");if (cur != null) {// Java中stack已过时,推荐用DequeDeque<Node> deque = new LinkedList<>();deque.push(cur);while (!deque.isEmpty()) {cur = deque.pop(); // 弹出栈顶元素并打印System.out.print(cur.value + ",");// 先压入右子树进栈,再压入左子树if (cur.right != null) {deque.push(cur.right);}if (cur.left != null) {deque.push(cur.left);}}}}// 中序遍历// 用栈结构,先把左子树全部压入栈,为空就弹出栈顶元素,再向右走public static void inOrder(Node cur) {System.out.print("中序遍历:");if (cur != null) {Deque<Node> deque = new LinkedList<>();while (!deque.isEmpty() || cur != null) {if (cur != null) {deque.push(cur);cur = cur.left;} else {cur = deque.pop();System.out.print(cur.value + ",");cur = cur.right;}}}}// 后序遍历// 用两个栈,很巧妙,能看懂,但写不出public static void postOrder(Node cur) {System.out.print("后序遍历:");if (cur != null) {Deque<Node> deque1 = new LinkedList<>();Deque<Node> deque2 = new LinkedList<>();deque1.push(cur);while (!deque1.isEmpty() ) {cur = deque1.pop();deque2.push(cur); // 全部压入栈2,顺序是头 右 左if (cur.left != null) { // 先压入栈1,又被压入栈2deque1.push(cur.left);}if (cur.right != null) {deque1.push(cur.right);}}// 栈2,打印结果:左 右 头while (!deque2.isEmpty()) {cur = deque2.pop();System.out.print(cur.value + ",");}}}public static void postOrder2(Node h) {System.out.print("后序遍历2: ");if (h != null) {Deque<Node> stack = new LinkedList<>();stack.push(h);Node c = null;while (!stack.isEmpty()) {c = stack.peek();if (c.left != null && h != c.left && h != c.right) {stack.push(c.left);} else if (c.right != null && h != c.right) {stack.push(c.right);} else {System.out.print(stack.pop().value + " ");h = c;}}}}public static void main(String[] args) {// 创建一个满二叉树Node head = new Node(1);head.left = new Node(2);head.right = new Node(3);head.left.left = new Node(4);head.left.right = new Node(5);head.right.left = new Node(6);head.right.right = new Node(7);// 经过先序遍历后,cur会指向最末尾的结点,为什么head的指向不变呢?// 网上查了一下,说Java是值传递,基本类型传递值,引用类型传递的是引用的拷贝preOrder(head); // 先序遍历System.out.println();inOrder(head);System.out.println();postOrder(head);System.out.println();postOrder2(head);System.out.println();levelOrder(head);System.out.println();}}
2021年12月上传的算法视频课:面试官急了,算法大神左神(左程云)耗时3个月打造100道算法与数据结构大厂高频面试题精讲,不出意外十年后仍是最好的算法刷题教程!
https://www.bilibili.com/video/BV1vi4y1R7g9?p=4&spm_id_from=pageDriver
