数组和字符串
反转数组
- 采用俩个指针,分别指向头和尾。移动进行交换
- 下标查找的时间复杂度是O1
- 查询是否存在的时间复杂度On
leecode242. 有效的字母异位词
方法:
创建26位的int数组,用来存放对应第几个字母的个数
遍历 s让相应的值++ ,遍历 t让相应的值 - -
最后如果数组中有一个值不为0则返回false
//给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
//
// 示例 1:
//
// 输入: s = "anagram", t = "nagaram"
//输出: true
//
//
// 示例 2:
//
// 输入: s = "rat", t = "car"
//输出: false
//
// 说明:
//你可以假设字符串只包含小写字母。
class Solution {
public boolean isAnagram(String s, String t) {
//用来存放string中26个英文字母的数量
int[] num=new int[26];
char[] chars=s.toCharArray();
for (int i = 0; i < chars.length; i++) {
num[chars[i]-'a']++;
}
char[] chars1 = t.toCharArray();
for (int i = 0; i < chars1.length; i++) {
num[chars1[i]-'a']--;
}
for (int i = 0; i < num.length; i++) {
if(num[i]!=0) return false;
}
return true;
}
}
链表
- 灵活的分配内存空间
- 前一个元素已知,能在O1时间内删除或添加元素
- 链表不能快速查询,查询元素需要On的时间
结题技巧
- 利用快慢指针,有时候需要用到三个指针
链表的反转,寻找倒数第k个元素,判断链表是否有环 - 构建一个虚假的链表头
给定排好序的链表,将他们整合 并且排序
将链表的奇偶数按原定顺序分离,生成前半部分为奇数,后半部分为偶数的链表
在解决链表的题目时,在纸上或者白板上画出节点之间的相互关系
leecode25. K个一组翻转链表
方法:
用三个指针,前指针,当前指针,下一个指针
开始前指针指向第一个指针的上一个,当前指针和下一个指针遍历,进行翻转,结束后,下一个指针指向了下一个K的头,当前指针(原来的尾,现在的头)与前指针相连,原来的头(现在的尾)与下一个指针相连。
继续遍历下一个K。
//给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
//
// k 是一个正整数,它的值小于或等于链表的长度。
//
// 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
//
//
//
// 示例:
//
// 给你这个链表:1->2->3->4->5
//
// 当 k = 2 时,应当返回: 2->1->4->3->5
//
// 当 k = 3 时,应当返回: 3->2->1->4->5
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode pre=dummy;//表示头节点
ListNode now=head;//表示当前节点
ListNode next=head;//表示下一个的头节点
ListNode end=dummy;//表示最后一个节点
while(end!=null){
for (int i = 0; i <k && end!=null; i++) end=end.next;
if(end==null) break;// end为空,则长度不够,不反转
next=end.next;//存下一个的头
end.next=null;//让尾巴的下一个节点等于null
reserve(now);
pre.next=end;
now.next=next;
pre=now;
end=now;
now=next;
}
return dummy.next;
}
void reserve(ListNode now){
ListNode next=null;
ListNode temp=new ListNode(0);
while (now!=null){
next=now.next;
now.next=temp;
temp=now;
now=next;
}
}
}
栈
- 后进先出,只能操作栈顶
- 当在解决某个问题的时候,只关心最近一次的操作,并且在处理完上一次操作后,能在O1时间内查找到更前一次的操作
leecode20.有效的括号
方法:
不断的压入左括号,遇到相应的右括号则弹出。如果最后栈为空,则正确,否则错误
leecode739.每日温度
方法:
用栈来进行操作,将值存在数组中。栈中存放的元素除了值以外还得存下标。所以存Entry
当遇到比栈顶温度低的,存入栈顶
while(当遇到比栈顶温度高时) { 吐出栈顶,对应下标的值为 当前下标减去栈顶下标 }
//请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
//
//
// 例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2
//, 1, 1, 0, 0]。
class Solution {
class Entry{
int value;
int index;
public Entry(int value,int index){
this.value=value;
this.index=index;
}
}
public int[] dailyTemperatures(int[] T) {
int len=T.length;
if(len==0) return null;
int[] result=new int[len];
Stack<Entry> stack=new Stack<>();
stack.push(new Entry(T[0], 0));
for (int i = 1; i <len ; i++) {
while (!stack.isEmpty() && stack.peek().value < T[i]) {
result[stack.peek().index] =i-stack.peek().index;
stack.pop();
}
stack.push(new Entry(T[i],i));
}
return result;
}
}
队列
- 先进先出 用LinkedList实现
- 常用场景:广度优先遍历
双端队列
- LInkedList实现,需要对队列的头尾俩端能在O1的时间内进数据的操作
- 实现一个长度动态变化的窗口或者连续区间
leecode239.滑动窗口最大值
实现:
利用双端队列来表示这个窗口,用List存返回的列表
先全部添加到窗口中,添加时,while(下一个元素比链表的尾大) {把前面尾删了},再把下一个元素加入进去。
因为在没移动前就需要增加当前的最大值,所以list先增加一下链表的头
移动:添加的时候:while(下一个元素比链表的尾大) {把前面尾删了}, 如果移动的时候queue.getFirst()==nums[i-k]
则删除双端链表的头,再把下一个元素加入进去。list增加链表的头
到底后,把list返回
//给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
//
// 返回滑动窗口中的最大值。
//
// 示例:
//
// 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
//输出: [3,3,5,5,6,7]
//解释:
//
// 滑动窗口的位置 最大值
//--------------- -----
//[1 3 -1] -3 5 3 6 7 3
// 1 [3 -1 -3] 5 3 6 7 3
// 1 3 [-1 -3 5] 3 6 7 5
// 1 3 -1 [-3 5 3] 6 7 5
// 1 3 -1 -3 [5 3 6] 7 6
// 1 3 -1 -3 5 [3 6 7] 7
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
LinkedList<Integer> queue=new LinkedList<>();
ArrayList<Integer> result=new ArrayList<>();
for (int i = 0; i <k ; i++) {
if(i==0) {
queue.add(nums[0]);
continue;
}
while (!queue.isEmpty() && queue.getLast()<nums[i]){
queue.removeLast();
}
queue.add(nums[i]);
}
result.add(queue.getFirst());
for (int i = k; i <nums.length ; i++) {
while (!queue.isEmpty() && queue.getLast()<nums[i]){
queue.removeLast();
}
queue.add(nums[i]);
if(queue.getFirst()==nums[i-k]) queue.removeFirst();
result.add(queue.getFirst());
}
int[] rr=new int[result.size()];
for (int i = 0; i <rr.length ; i++) {
rr[i]=result.get(i);
}
return rr;
}
}
树
- 树有前序中序和后序。可以调用递归
- 平衡二叉树
- 完全二叉树
- 二叉搜索树
- 四叉树
遍历: - 中序遍历是顺序的
- leecode250 必须得看
void toArray(TreeNode root,List<Integer> list){
if(root==null) return;
toArray(root.left,list);
list.add(root.val); //添加在中间是中序,添加在前面是前序,添加在后面是后序
toArray(root.right,list);
}
leecode230.二叉搜索中第K小的元素
方法:
二叉树中序遍历,添加到list中 返回第k-1个元素
如果这道题变成了 搜索第K大的元素:则将其变成了反向的中序遍历:右 ;add();左;
//给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
//
// 说明:
//你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
//
// 示例 1:
//
// 输入: root = [3,1,4,null,2], k = 1
// 3
// / \
// 1 4
// \
// 2
//输出: 1
class Solution {
public int kthSmallest(TreeNode root, int k) {
List<Integer> list=new ArrayList<>();
toArray(root, list);
return list.get(k-1);
}
void toArray(TreeNode root,List<Integer> list){
if(root==null) return;
toArray(root.left,list);
list.add(root.val);
toArray(root.right,list);
}
}