剑指 Offer + TOP100
- 栈与队列
- 哈希
- 链表
- 字符串
- 查找
- 搜索:深度优先和广度优先
-
- 剑指 Offer 32 - I. 从上到下打印二叉树
- 剑指 Offer 32 - II. 从上到下打印二叉树 II (102. 二叉树的层序遍历)
- 剑指 Offer 32 - III. 从上到下打印二叉树 III
- 199. 二叉树的右视图
- 剑指 Offer 26. 树的子结构
- 剑指 Offer 27. 二叉树的镜像
- 剑指 Offer 28. 对称的二叉树
- 剑指 Offer 13. 机器人的运动范围
- 剑指 Offer 34. 二叉树中和为某一值的路径
- 剑指 Offer 36. 二叉搜索树与双向链表
- 剑指 Offer 54. 二叉搜索树的第k大节点
- 剑指 Offer 55 - I. 二叉树的深度
- 剑指 Offer 55 - II. 平衡二叉树
- 129. 求根节点到叶节点数字之和
- 543. 二叉树的直径
- 剑指 Offer 64. 求1+2+…+n
- 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
- 剑指 Offer 68 - II. 二叉树的最近公共祖先(236. 二叉树的最近公共祖先)
- 200. 岛屿数量
- 33. 搜索旋转排序数组
- 94. 二叉树的中序遍历
- 124. 二叉树中的最大路径和
- 144. 二叉树的前序遍历
- 112. 路径总和
- 98. 验证二叉搜索树
- 662. 二叉树最大宽度
- 滑动窗口
- 剪枝和回溯
- 动态规划
-
- 剑指 Offer 10- I. 斐波那契数列
- 剑指 Offer 10- II. 青蛙跳台阶问题
- 剑指 Offer 63. 股票的最大利润(121. 买卖股票的最佳时机)
- 122. 买卖股票的最佳时机 II
- 123. 买卖股票的最佳时机 III
- 剑指 Offer 42. 连续子数组的最大和(53. 最大子数组和)
- 152. 乘积最大子数组
- 剑指 Offer 47. 礼物的最大价值
- 剑指 Offer 46. 把数字翻译成字符串
- 300. 最长递增子序列
- 72. 编辑距离
- 1143. 最长公共子序列
- 64. 最小路径和
- 718. 最长重复子数组
- 322. 零钱兑换
- 62. 不同路径
- 221. 最大正方形
- 139. 单词拆分
- 双指针
- 排序
- 递归和分治
- 模拟
- 数学
栈与队列
剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
利用其先进后出的特性,两个Stack完成先进先出的特性
class CQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public CQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void appendTail(int value) {
stack1.push(value);
}
public int deleteHead() {
if(stack2.isEmpty()){
if(stack1.isEmpty()) return -1;
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
class MyQueue {
Stack<Integer> stack1;
Stack<Integer> stack2;
public MyQueue() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void push(int x) {
stack1.push(x);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
public int peek() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.peek();
}
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}
剑指 Offer 30. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
需要维护两个栈结构,其中一个维护最小值的栈,需要关注普通栈的出入栈情况与自身的情况。
Stack类中使用 peek、push、pop方法
class MinStack {
Stack<Integer> stack1;
Stack<Integer> stack2;
/** initialize your data structure here. */
public MinStack() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
public void push(int x) {
if(stack2.isEmpty() || stack2.peek() >= x){
stack2.push(x);
}
stack1.push(x);
}
public void pop() {
if(stack1.pop().equals(stack2.peek())){
stack2.pop();
}
}
public int top() {
return stack1.peek();
}
public int min() {
return stack2.peek();
}
}
剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
//单调队列
//下面是要注意的点:
//队列按从大到小放入
//如果首位值(即最大值)不在窗口区间,删除首位
//如果新增的值小于队列尾部值,加到队列尾部
//如果新增值大于队列尾部值,删除队列中比新增值小的值,如果在把新增值加入到队列中
//如果新增值大于队列中所有值,删除所有,然后把新增值放到队列首位,保证队列一直是从大到小
if (nums.length == 0) return nums;
Deque<Integer> deque = new LinkedList<>();
int[] arr = new int[nums.length - k + 1];
int index = 0; //arr数组的下标
//未形成窗口区间
for (int i = 0; i < k; i++) {
//队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值
//一直循环删除到队列中的值都大于当前值,或者删到队列为空
while (!deque.isEmpty() && nums[i] > deque.peekLast()) deque.removeLast();
//执行完上面的循环后,队列中要么为空,要么值都比当前值大,然后就把当前值添加到队列中
deque.addLast(nums[i]);
}
//窗口区间刚形成后,把队列首位值添加到队列中
//因为窗口形成后,就需要把队列首位添加到数组中,而下面的循环是直接跳过这一步的,所以需要我们直接添加
arr[index++] = deque.peekFirst();
//窗口区间形成
for (int i = k; i < nums.length; i++) {
//i-k是已经在区间外了,如果首位等于nums[i-k],那么说明此时首位值已经不再区间内了,需要删除
if (deque.peekFirst() == nums[i - k]) deque.removeFirst();
//删除队列中比当前值大的值
while (!deque.isEmpty() && nums[i] > deque.peekLast()) deque.removeLast();
//把当前值添加到队列中
deque.addLast(nums[i]);
//把队列的首位值添加到arr数组中
arr[index++] = deque.peekFirst();
}
return arr;
}
}
剑指 Offer 59 - II. 队列的最大值
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
维护一个递减的双端队列存放MAX即可,思路仿照min的栈
class MaxQueue {
Queue<Integer> queue;
Deque<Integer> deque;
public MaxQueue() {
queue = new LinkedList<>();
deque = new LinkedList<>();
}
public int max_value() {
return deque.isEmpty() ? -1 : deque.peekFirst();
}
public void push_back(int value) {
queue.add(value);
while(!deque.isEmpty() deque.peekLast() < value)
deque.pollLast();
deque.addLast(value);
}
public int pop_front() {
if(queue.isEmpty()) return -1;
if(queue.peek().equals(deque.peekFirst())) deque.pollFirst();
return queue.poll();
}
}
146. LRU 缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
这题虽然是让你写一种缓存算法,但其实是纯粹的数据结构考察,做题时先给面试官讲一遍LRU和LinkedHashMap的八股,再自己使用双向链表实现就好,考虑自己写输入输出
最简单的做法是调用LinkedHashMap当然,面试提一嘴就可以。
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
- 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
- 哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
- 在双向链表的实现中,使用一个伪头部(dummy head)和伪尾部(dummy tail)标记界限,这样在添加节点和删除节点的时候就不需要检查相邻的节点是否存在。
class LRUCache {
//创建双向链表
class DLinkedNode{
int key;
int value;
DLinkedNode pre;
DLinkedNode next;
public DLinkedNode(){
}
public DLinkedNode(int key,int value){
this.key = key;
this.value = value;
}
}
//创建一个HashMap
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size; //当前数据结构的大小
private int capacity;//当前数据结构的容量
private DLinkedNode head,tail; //创建虚拟的头尾节点,方便操作
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
//使用伪头部节点和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
//节点连接
head.next = tail;
tail.pre = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if(node == null){
return -1;
}
//如果存在需要移动到链表头部
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if(node == null){
//如果key不存在,创建新的节点
DLinkedNode newNode = new DLinkedNode(key,value);
//添加到哈希表
cache.put(key,newNode);
//添加到头部
addToHead(newNode);
size++;
if(size > capacity){
DLinkedNode tail = removeTail();
cache.remove(tail.key);
--size;
}
}else{
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node){
node.pre = head;
node.next = head.next;
node.next.pre = node;
head.next = node;
}
private void removeNode(DLinkedNode node){
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void moveToHead(DLinkedNode node){
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail(){
DLinkedNode res = tail.pre;
removeNode(res);
return res;
}
}
20. 有效的括号
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
class Solution {
public boolean isValid(String s) {
if(s.isEmpty()) return true;
Stack<Character> stack = new Stack<>();
for(char c : s.toCharArray()){
if(c == '(') stack.push(')');
else if(c == '{') stack.push('}');
else if(c == '[') stack.push(']');
else if(stack.isEmpty() || c != stack.pop()) return false;
}
if(stack.isEmpty()) return true;
return false;
}
}
32. 最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
class Solution {
public int longestValidParentheses(String s) {
if(s == null || s.length() == 0) return 0;
Deque<Integer> stack = new ArrayDeque<>();
stack.push(-1);
int res = 0;
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == '(') stack.push(i);
else{
stack.pop();
if(stack.isEmpty())stack.push(i);
else{
res = Math.max(res, i - stack.peek());
}
}
}
return res;
}
}
class Solution {
public int longestValidParentheses(String s) {
if(s == null || s.length() == 0) return 0;
int[] dp = new int[s.length()];
int res = 0;
for(int i = 0; i < s.length(); i++){
if(i > 0 && s.charAt(i) == ')'){
if(s.charAt(i-1) == '('){
dp[i] = (i - 2 >= 0 ? dp[i-2] + 2 : 2);
}else if(s.charAt(i - 1) == ')' && i - dp[i-1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '('){
dp[i] = dp[i - 1] + 2 + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
227. 基本计算器 II
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
739. 每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指在第 i 天之后,才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int[] res = new int[temperatures.length];
Deque<Integer> stack = new LinkedList<>();
for(int i = 0; i < temperatures.length; i++){
//递减栈
while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){
res[stack.peek()] = i - stack.pop();
}
stack.push(i);
}
return res;
}
}
哈希
41. 缺失的第一个正数
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
由于时间与空间复杂度的限制,同时可以得出正整数会出现在[1,N+1]之间,因此使用原地哈希的方法,即把数组看成一个哈希表
class Solution {
public int firstMissingPositive(int[] nums) {
int len = nums.length;
for(int i = 0; i < len; i++){
while(nums[i] > 0 && nums[i] <= len && nums[nums[i] -1] != nums[i]){
swap(nums,i,nums[i] - 1);
}
}
for(int i = 0; i < len ; i++){
if(nums[i] != i + 1) return i + 1;
}
return len + 1;
}
private void swap(int[] nums,int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
128. 最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
首先去重,因为判断最长序列,遍历到一个数
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> num_set = new HashSet<Integer>();
for(int num : nums){
num_set.add(num);
}
int longestStreak = 0;
for(int num : num_set){
if (!num_set.contains(num - 1)) {
int currentNum = num;
int currentStreak = 1;
while (num_set.contains(currentNum + 1)) {
currentNum += 1;
currentStreak += 1;
}
longestStreak = Math.max(longestStreak, currentStreak);
}
}
return longestStreak;
}
}
链表
剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
返回链表节点的数组,因此选用栈结构来达到逆序的效果,与下题(24)的反转链表并不相同。
class Solution {
public int[] reversePrint(ListNode head) {
if(head == null) return new int[0];
Stack<Integer> stack = new Stack<>();
while(head != null){
stack.push(head.val);
head = head.next;
}
int[] res = new int[stack.size()];
for(int i=0;i<res.length;i++){
res[i] = stack.pop();
}
return res;
}
}
剑指 Offer 24. 反转链表 (206. 反转链表)
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
返回节点头,因此需要一个指针记录头节点的位置,同时使用头插法完成逆序。
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) return null;
ListNode res = new ListNode(0);
ListNode cur = head;
ListNode tmp = head;
while(cur != null){
tmp = cur.next;
cur.next = res.next;
res.next = cur;
cur= tmp;
}
return res.next;
}
public ListNode reverseList(ListNode head) {
ListNode cur = head,pre = null;
while(cur != null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
public ListNode reverseList(ListNode head) {
return recur(head,null);
}
private ListNode recur(ListNode cur, ListNode pre){
if(cur == null) return pre;
ListNode res = recur(cur.next,cur);
cur.next = pre;
return res;
}
}
92.反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode res = new ListNode(0);
res.next = head;
ListNode g = res;
ListNode p = res.next;
for(int step = 1; step < left; step++){
g = g.next; //g指向反转节点的前一个节点
p = p.next;
}
//使用头插法
for(int i = 0; i < right - left;i++){
ListNode cur = p.next; //将cur插入到队列中
p.next = p.next.next;
cur.next = g.next;
g.next = cur;
}
return res.next;
}
}
剑指 Offer 35. 复杂链表的复制
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
因为随机指针的加入,并不可以依次创建节点再链接,此题中使用了HashMap进行储存节点,key->旧节点,value->新节点,并进行指针的赋值,完成操作,最终返回我们的头结点即可。
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
Node cur = head;
Map<Node,Node> map = new HashMap<>();
while(cur != null){
map.put(cur,new Node(cur.val));
cur = cur.next;
}
cur = head;
while(cur != null){
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
25. K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
变形题:不足K个也要翻转
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
//设置链表虚拟头,方便返回与指针的移动
ListNode newHead = new ListNode(0);
newHead.next = head;
//需要两个指针指向目前需要翻转的链表的头部和尾部
ListNode pre = newHead;
ListNode end = newHead;
while(end.next != null){
for(int i = 0; i < k && end != null; i++) end = end.next; //移动到链表的最后一个元素
if(end == null) break;
ListNode start = pre.next; //翻转链表的第一个元素
ListNode next = end.next;//下一个翻转链表的第一个元素
end.next = null;
pre.next = reverse(start); //相当于前一个链表的尾部的下一个元素是新链表的头节点
start.next = next; //此时start的下一个为null,指向新的链表的头节点
pre = start; //继续移动end和pre,此时都应处理反转后的链表的尾节点,即为start
end = pre;
}
return newHead.next;
}
//链表翻转模板
private ListNode reverse(ListNode head){
ListNode cur = head;
ListNode pre = null;
while(cur != null){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
//递归终止
if(head == null) return null;
//找到翻转的尾节点
ListNode end = head;
for(int i = 0; i < k-1; i++){
end = end.next;
//不满足k个时
if(end == null){
return head;
}
}
//记录下个链表的头节点
ListNode next = end.next;
ListNode newHead = reverse(head,end);
//进行递归,此时的头节点变成了尾节点
head.next = reverseKGroup(next,k);
return newHead;
}
//链表翻转模板
private ListNode reverse(ListNode head,ListNode end){
ListNode cur = head;
ListNode pre = null;
while(pre != end){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
//递归终止
if(head == null) return null;
//找到翻转的尾节点
ListNode end = head;
for(int i = 0; i < k-1; i++){
end = end.next;
//不满足k个时,rrengyd aojinxingfanzhuan
if(end.next == null && i <= k - 2){
break;
}
}
//记录下个链表的头节点
ListNode next = end.next;
end.next = null;
ListNode newHead = reverse(head);
//进行递归,此时的头节点变成了尾节点
head.next = reverseKGroup(next,k);
return newHead;
}
//链表翻转模板
private ListNode reverse(ListNode head){
ListNode cur = head;
ListNode pre = null;
while(cur != null){
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
}
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
143. 重排链表
快慢指针找到链表中点+链表逆序 + 合并链表
class Solution {
public void reorderList(ListNode head) {
if(head == null || head.next == null ||head.next.next == null) return;
//快慢指针找到中点
ListNode slow = head;
ListNode fast = head;
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
ListNode newHead = slow.next;
slow.next = null;
//后面的链表进行倒置
newHead = reverseList(newHead);
//两个链表进行合并
while(newHead != null){
ListNode node = newHead.next;
newHead.next = head.next;
head.next = newHead;
head = newHead.next;
newHead = node;
}
}
private ListNode reverseList(ListNode head){
ListNode pre = null;
ListNode cur = head;
while(cur != null){
ListNode node = cur.next;
cur.next = pre;
pre = cur;
cur = node;
}
return pre;
}
}
class Solution {
public void reorderList(ListNode head) {
if(head == null || head.next == null ||head.next.next == null) return;
List<ListNode> list = new ArrayList<>();
while(head != null){
list.add(head);
head = head.next;
}
int i = 0, j = list.size() - 1;
while(i < j){
list.get(i).next = list.get(j);
i++;
if(i == j) break;
list.get(j).next = list.get(i);
j--;
}
list.get(i).next = null;
}
}
82. 删除排序链表中的重复元素 II
存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。
返回同样按升序排列的结果链表。
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) return head;
//当前节点和下一个节点的值比较,若不同head保留,对head.next进行递归
if(head.val != head.next.val){
head.next = deleteDuplicates(head.next);
return head;
}else{
//当前节点与下一节点的值重复。一直向下找,找到不重复的节点
ListNode notDup = head.next.next;
while(notDup != null && notDup.val == head.val){
notDup = notDup.next;
}
return deleteDuplicates(notDup);
}
}
}
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null) return head;
ListNode res = new ListNode(0);
res.next = head;
ListNode pre = res;
ListNode cur = head;
while(cur != null){
while(cur.next != null && cur.next.val == cur.val) cur = cur.next;
if(pre.next == cur) pre = pre.next;
else pre.next = cur.next;
cur = cur.next;
}
return res.next;
}
}
61. 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置
链表成环,再寻找新的尾部节点以及新的头节点
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if(head == null || head.next == null) return head;
if(k == 0) return head;
ListNode tail = head, newTail = head;
ListNode newHead;
int n = 1;
while(tail.next != null){
tail = tail.next;
n++;
}
tail.next = head;
for(int i = 0; i < (n - k%n - 1); i++){
newTail = newTail.next;
}
newHead = newTail.next;
newTail.next = null;
return newHead;
}
}
328. 奇偶链表
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
class Solution {
public ListNode oddEvenList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode oddHead = head,oddTail = oddHead;
ListNode evenHead = head.next, evenTail = evenHead;
for(ListNode cur = head.next.next; cur != null;){
oddTail.next = cur;
oddTail = oddTail.next;
cur = cur.next;
if(cur != null){
evenTail.next = cur;
evenTail = evenTail.next;
cur = cur.next;
}
}
oddTail.next = evenHead;
evenTail.next = null;
return oddHead;
}
}
字符串
剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
一般会使用StringBuilder类进行解答
class Solution {
public String replaceSpace(String s) {
StringBuffer res = new StringBuffer();
char[] strs = s.toCharArray();
for(char str:strs){
if(str == ' '){
res.append("%20");
}else{
res.append(str);
}
}
return res.toString();
}
}
剑指 Offer 58 - II. 左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
StringBuilder + substring
class Solution {
public String reverseLeftWords(String s, int n) {
StringBuilder res = new StringBuilder();
res.append(s.substring(n));
res.append(s.substring(0,n));
return res.toString();
}
}
415. 字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
双指针 ,指向俩个数组的尾部
class Solution {
public String addStrings(String num1, String num2) {
StringBuilder res = new StringBuilder("");
int i = num1.length() - 1, j = num2.length() -1,carry = 0;
while(i >= 0 || j >= 0){
int n1 = i >= 0 ? num1.charAt(i)-'0' : 0;
int n2 = j >= 0 ? num2.charAt(j)-'0' : 0;
int tmp = n1 + n2 + carry;
carry = tmp / 10;
res.append(tmp % 10);
i--;
j--;
}
if(carry == 1) res.append(1);
return res.reverse().toString();
}
}
5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
动态规划和中心扩展法两种需要掌握
class Solution {
public String longestPalindrome(String s) {
//动态规划的解法 dp[i][j]表示字串s[i...j]是否为回文子串
//dp[i][j] 取决于 dp[i+1][j-1]
int len = s.length();
if(len < 2) return s;
int maxLen = 1;
int begin = 0;
boolean[][] dp = new boolean[len][len];
char[] charArray = s.toCharArray();
for(int i = 0; i < len ;i++){
dp[i][i] = true;
}
//下三角矩阵
for(int j = 1;j < len; j++){
for(int i = 0; i < j;i++){
if(charArray[i] != charArray[j]){
dp[i][j] = false;
}else{
if(j - i < 3){
dp[i][j] = true;
}else{
dp[i][j] = dp[i+1][j-1];
}
}
if(dp[i][j] && j - i + 1 > maxLen){
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0,end = 0;
for(int i = 0; i < s.length(); i++){
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if(len > end - start){
start = i - (len - 1)/2;
end = i + len /2;
}
}
return s.substring(start,end+1);
}
//双边扩展,返回回文子串的长度
private int expandAroundCenter(String s, int left, int right) {
int i = left , j = right;
while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
i--;
j++;
}
return j - i -1;
}
}
8. 字符串转换整数 (atoi)
class Solution {
public int myAtoi(String s) {
int len = s.length();
char[] strs = s.toCharArray(