持续更新中....................
剑指Offer 9. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
class CQueue {
Stack<Integer> stackIn;
Stack<Integer> stackOut;
public CQueue() {
stackIn = new Stack<Integer>();
stackOut = new Stack<Integer>();
}
public void appendTail(int value) {
stackIn.push(value);
}
public int deleteHead() {
if(stackOut.isEmpty()){
while(!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
return stackOut.isEmpty() ? -1 : stackOut.pop();
}
}
剑指 Offer 30. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
class MinStack {
Stack<Integer> stack;
Stack<Integer> minStack;
public MinStack() {
stack = new Stack<Integer>();
minStack = new Stack<Integer>();
minStack.push(Integer.MAX_VALUE);
}
public void push(int x) {
stack.push(x);
minStack.push(Math.min(minStack.peek(), x));
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int min() {
return minStack.peek();
}
}
剑指Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
while(head != null){
ListNode temp = head.next;
head.next = pre;
pre = head;
head = temp;
}
return pre;
}
}
剑指 Offer 6. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
//2ms
class Solution {
public int[] reversePrint(ListNode head) {
Stack<Integer> stack = new Stack<Integer>();
int size = 0;
while(head != null){
size++;
stack.push(head.val);
head = head.next;
}
int[] result = new int[size];
for(int i = 0; i < size; i++){
result[i] = stack.pop();
}
return result;
}
}
//0ms
class Solution {
public int[] reversePrint(ListNode head) {
int size = 0;
ListNode pre = null;
while(head != null){
size++;
ListNode temp = head.next;
head.next = pre;
pre = head;
head = temp;
}
int[] result = new int[size];
for(int i = 0; i < size; i++){
result[i] = pre.val;
pre = pre.next;
}
return result;
}
}
剑指Offer 35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
class Solution {
public Node copyRandomList(Node head) {
Map<Node, Node> map = new HashMap<>();
Node cur = head;
while(cur != null){
map.put(cur, new Node(cur.val)); //复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
cur = cur.next;
}
cur = head;
while(cur != null){ //构建新链表的 next 和 random 指向
Node node = map.get(cur);
node.next = map.get(cur.next);
node.random = map.get(cur.random);
cur = cur.next;
}
return map.get(head); //返回新链表的头节点
}
}
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
剑指Offer 05. 替换空格
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
class Solution {
public String replaceSpace(String s) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) != ' '){
sb.append(s.charAt(i));
}else{
sb.append("%20");
}
}
return sb.toString();
}
}
剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例1:输入: s = "abcdefg", k = 2 输出: "cdefgab"
class Solution {
public String reverseLeftWords(String s, int n) {
char[] chars = s.toCharArray();
reverse(chars, 0, chars.length - 1);
reverse(chars, 0, chars.length - n - 1);
reverse(chars, chars.length - n, chars.length - 1);
return String.valueOf(chars);
}
public void reverse(char[] chars, int left, int right){
while(left < right){
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
}
剑指Offer 3. 数组中重复的数字
找出数组中重复的数字。在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
class Solution {
public int findRepeatNumber(int[] nums) {
int i = 0;
while(i < nums.length){
if(nums[i] == i){
i++;
continue;
}
if(nums[nums[i]] == nums[i]){
return nums[i];
}else{
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
class Solution {
public int search(int[] nums, int target) {
int count = 0;
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target){
count++;
left = mid - 1;
right = mid + 1;
while(left >= 0 && nums[left] == target){
left--;
count++;
}
while(right < nums.length && nums[right] == target){
right++;
count++;
}
return count;
}else if(nums[mid] > target){
right = mid - 1;
}else if(nums[mid] < target){
left = mid + 1;
}
}
return count;
}
}
剑指 Offer 53 - II. 0~n-1中缺失的数字
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例1:输入: [0,1,3] 输出: 2
示例2:输入: [0,1] 输出: 2
class Solution {
public int missingNumber(int[] nums) {
for(int i = 0; i < nums.length; i++){
if(nums[i] != i){
return i;
}
}
return nums.length;
}
}
剑指 Offer 04. 二维数组中的查找
在一个 n * m 的二维数组中,每一行都按照从左到右 非递减 的顺序排序,每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int i = matrix.length - 1; //从矩阵左下角的坐标开始
int j = 0;
while(i >= 0 && j < matrix[0].length){
if(matrix[i][j] == target){
return true;
}else if(matrix[i][j] > target){
i--;
}else if(matrix[i][j] < target){
j++;
}
}
return false;
}
}
剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。
class Solution {
public int minArray(int[] numbers) {
int left = 0;
int right = numbers.length - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(numbers[mid] > numbers[right]){
left = mid + 1;
}else if(numbers[mid] < numbers[right]){
right = mid;
}else{
right--;
}
}
return numbers[left];
}
}
剑指 Offer 50. 第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例:输入:s = "abaccdeff" 输出:'b'
class Solution {
public char firstUniqChar(String s) {
int[] array = new int[26];
for(int i = 0; i < s.length(); i++){
array[s.charAt(i) - 'a']++;
}
for(int i = 0; i < s.length(); i++){
if(array[s.charAt(i) - 'a'] == 1){
return s.charAt(i);
}
}
return ' ';
}
}
剑指 Offer 32 - I. 从上到下打印二叉树
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
class Solution {
public int[] levelOrder(TreeNode root) {
if(root == null){
return new int[]{};
}
List<Integer> list = new ArrayList<>();
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
while(!deque.isEmpty()){
int size = deque.size();
for(int i = 0; i < size; i++){
TreeNode node = deque.poll();
list.add(node.val);
if(node.left != null){
deque.offer(node.left);
}
if(node.right != null){
deque.offer(node.right);
}
}
}
return list.stream().mapToInt(x -> x).toArray();
}
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if(root == null){
return list;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
while(!deque.isEmpty()){
List<Integer> item = new ArrayList<>();
int size = deque.size();
for(int i = 0; i < size; i++){
TreeNode node = deque.poll();
item.add(node.val);
if(node.left != null){
deque.offer(node.left);
}
if(node.right != null){
deque.offer(node.right);
}
}
list.add(item);
}
return list;
}
}
剑指 Offer 32 - III. 从上到下打印二叉树 III
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if(root == null){
return list;
}
Deque<TreeNode> deque = new LinkedList<>();
deque.offer(root);
int depth = 1;
while(!deque.isEmpty()){
Deque<Integer> item = new LinkedList<>();
int size = deque.size();
for(int i = 0; i < size; i++){
TreeNode node = deque.poll();
if(depth % 2 != 0){
item.offer(node.val);
}else{
item.offerFirst(node.val);
}
if(node.left != null){
deque.offer(node.left);
}
if(node.right != null){
deque.offer(node.right);
}
}
list.add(new ArrayList<>(item));
depth++;
}
return list;
}
}
剑指 Offer 26. 树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值。
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A == null || B == null){
return false;
}
if(isSameTree(A, B)){
return true;
}
return isSubStructure(A.left, B) || isSubStructure(A.right, B);
}
public boolean isSameTree(TreeNode p, TreeNode q){
if(q == null){
return true;
}
if(p == null || p.val != q.val){
return false;
}
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
//方法一
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return null;
}
mirrorTree(root.left);
mirrorTree(root.right);
swap(root);
return root;
}
public void swap(TreeNode root){
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
//方法二
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode temp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(temp);
return root;
}
}
剑指 Offer 28. 对称的二叉树
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return compare(root.left, root.right);
}
public boolean compare(TreeNode leftNode, TreeNode rightNode){
if(leftNode == null && rightNode == null){
return true;
}
if(leftNode == null || rightNode == null || leftNode.val != rightNode.val){
return false;
}
return compare(leftNode.left, rightNode.right) && compare(leftNode.right, rightNode.left);
}
}
剑指 Offer 18. 删除链表的节点
给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。
class Solution {
public ListNode deleteNode(ListNode head, int val) {
ListNode pre = new ListNode();
pre.next = head;
ListNode cur = pre;
while(cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return pre.next;
}
}
剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。
示例:给定一个链表: 1->2->3->4->5, 和 k = 2. 返回链表 4->5.
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fastIndex = head;
ListNode slowIndex = head;
for(int i = 0; i < k; i++){
fastIndex = fastIndex.next;
}
while(fastIndex != null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
return slowIndex;
}
}
剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例:输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode pre = new ListNode();
ListNode cur = pre;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
cur.next = l1;
l1 = l1.next;
}else{
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 != null ? l1 : l2;
return pre.next;
}
}
剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
class Solution {
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode A = headA;
ListNode B = headB;
while(A != B){
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}
return A;
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。
class Solution {
public int[] exchange(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right){
while(left < right && nums[left] % 2 != 0){
left++;
}
while(left < right && nums[right] % 2 == 0){
right--;
}
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
return nums;
}
}