数组
704.二分查找
class Solution {
public int search(int[] nums, int target) {
/* 确保不会出现target目标值超出范围 */
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}
/*解的是[l r]都是闭区间的情况,l == r 是有意义的。*/
int left = 0;
int right = nums.length - 1;
/*都是闭区间用<=*/
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target){
return mid;
}
else if(nums[mid] > target){
right = mid - 1; /*闭区间必须-1,因为会在while判断的时候再判断一次right,重复判断。*/
}
else{
left = mid + 1;
}
}
return -1;
}
}
左闭右开理解:
进
行
一
个
分
隔
27.移除元素
/*这是我自己的想法,两个指针从头尾双寻找,最后返回尾指针+1(数组是从0开始的,长度是算的总长度)*/
class Solution {
public int removeElement(int[] nums, int val) {
int finalLen = nums.length - 1;//切记一定要 -1,如果不 -1后面运行会报错,超出数组范围。
int originalLen = 0;
while(originalLen <= finalLen){
if(nums[originalLen] != val)
originalLen++;
else if(nums[originalLen] == val && nums[finalLen] != val){
nums[originalLen] = nums[finalLen];
finalLen--;
originalLen++;
}
else if(nums[originalLen] == val && nums[finalLen] == val)
finalLen--;
}
return finalLen + 1;
}
}
快慢指针理解:
class Solution {
public int removeElement(int[] nums, int val) {
// 快慢指针
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] != val) {
nums[slowIndex] = nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}
进
行
一
个
分
隔
977. 有序数组的平方
class Solution {
public int[] sortedSquares(int[] nums) {
/*暴力破解法*/
for(int i = 0; i < nums.length; i++)
{
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
/* 双指针方法 */
class Solution {
public int[] sortedSquares(int[] nums) {
int i = nums.length - 1;
int[] finalArray = new int[i + 1];
int head = 0 , tail = i;
int oneSqu = 0 , twoSqu = 0;
while(head <= tail){
oneSqu = nums[head] * nums[head];
twoSqu = nums[tail] * nums[tail];
if(oneSqu < twoSqu){
finalArray[i] = twoSqu;
tail--;
i--;
}
else{
finalArray[i] = oneSqu;
head++;
i--;
}
}
return finalArray;
}
}
双指针法理解:
进
行
一
个
分
隔
209.长度最小的子数组
class Solution {
public int minSubArrayLen(int target, int[] nums) {
//滑动窗口思想解决问题。
int result = Integer.MAX_VALUE;
int sum = 0; //数总和
int i = 0; //起始指针
int subLength = 0;
for(int j = 0; j < nums.length; j++) //j代表尾指针
{
sum += nums[j];
while(sum >= target)
{
subLength = (j - i + 1);
result = result < subLength ? result : subLength;
sum -= nums[i];
i++;
}
}
if( result == Integer.MAX_VALUE ) //找不到需要返回0
return 0;
return result;
}
}
滑动窗口思想:
进
行
一
个
分
隔
59.螺旋矩阵II
配图思考
class Solution {
//本题主打一个边界问题,此垃圾循环边界是旧边的最后一个,即新边的第一个。
public int[][] generateMatrix(int n) {
int nums[][] = new int[n][n];
int k = 1;
for(int i = 0; i < n; i++)
{
int m = n - 1 - i;//正向横
int h = n - 1 - i;//正向纵
int a = n - 1 - i;//反向横
int b = n - 1 - i;//反向纵
if(k == n*n)
{
nums[i][n-1-i] = k;
break;
}
for(int j = i;j < n-1-i; j++)
{
nums[i][j] = k;
k++;
m--;
}
for(int j = i;j < n-1-i; j++)
{
nums[j][n-1-i] = k;
k++;
h--;
}
for(int j = n-1-i;j > i; j--)
{
nums[n-1-i][j] = k;
k++;
a--;
}
for(int j = n-1-i;j > i; j--)
{
nums[j][i] = k;
k++;
b--;
}
}
return nums;
}
}
进
行
一
个
分
隔
链表
首先要学会如何建立链表
public class ListNode {
// 结点的值
int val;
// 下一个结点
ListNode next;
// 节点的构造函数(无参)
public ListNode() {
}
// 节点的构造函数(有一个参数)
public ListNode(int val) {
this.val = val;
}
// 节点的构造函数(有两个参数)
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
203.移除链表元素
class Solution {
public ListNode removeElements(ListNode head, int val) {
//不是if的原因是为了应对整个链全是要删除的情况。
while (head != null && head.val == val) { // 注意这里不是if
head = head.next;
}
ListNode replace = head;
while(replace != null){
//replace的下一个不能为空先判断的原因是,如果是空next.val读取不到数据,因为下一个指针是空。
//head在最开始已经排除了是要删除的对象,所以可以这么考虑。
if(replace.next !=null && replace.next.val == val){
replace.next = replace.next.next;
}
else
replace = replace.next;
}
return head;
}
}
/*设置虚拟头结点方法*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(); // 设置一个虚拟头结点
dummyHead.next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode cur = dummyHead;
while (cur.next != null) {
if(cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
head = dummyHead.next;
return head;
}
}
进
行
一
个
分
隔
707.设计链表
class ListNode {
int val;
ListNode next;
ListNode(){}
ListNode(int val) {
this.val=val;
}
}
class MyLinkedList {
int size; //链表下标
ListNode head;//虚拟链表头结点。注意:可以吧虚拟表头理解为下标为-1。
public MyLinkedList() {//初始化链表
size = 0;
head = new ListNode(0);
}
public int get(int index) {得到下标为index位置的数值,不存在则返回-1
ListNode searchNum = head.next;
if(index >= size)
return -1;
for(int i = 0; i < index; i++)
searchNum = searchNum.next;
return searchNum.val;
}
public void addAtHead(int val) {//添加头结点
addAtIndex(0,val);
/* ListNode newNode = new ListNode(val);
if(head.next == null){
head.next = newNode;
size++;
}
else{
newNode.next = head.next;
head.next = newNode;
size++;
}*/
}
public void addAtTail(int val) {//添加尾结点
addAtIndex(size,val);
/* ListNode replace = head;
ListNode newNode = new ListNode(val);
newNode.next = null;
while(replace.next != null){
replace = replace.next;
}
replace.next = newNode;
size++;*/
}
public void addAtIndex(int index, int val) {//在下标位置添加节点
if(index <= size){
//if(index == size)
//addAtTail(val);
//else{
ListNode newNode = new ListNode(val);
ListNode replace = head;
for(int i = 0;i < index;i++){
replace = replace.next;
}
newNode.next = replace.next;
replace.next = newNode;
size++;
//}
}
}
public void deleteAtIndex(int index) {//删除下标为index的节点
if(index < size){
ListNode replace = head;
for(int i = 0;i < index;i++){
replace = replace.next;
}
replace.next = replace.next.next;
size--;
}
}
}
进
行
一
个
分
隔
206.反转链表
class Solution {
//双指针的写法
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;//中转节点
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
}
}
进
行
一
个
分
隔
24. 两两交换链表中的节点
class Solution {
//设置一个虚拟头结点好做题
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode cur = dummyHead;
ListNode tmp;//临时节点
//思路:临时节点=头结点,虚拟头指向第二个节点,临时节点指向第三个节点,第二个节点指向临时节点
while(cur.next != null && cur.next.next !=null){
tmp = cur.next;
cur.next = tmp.next;
tmp.next = cur.next.next;
cur.next.next = tmp;
cur = cur.next.next;
}
return dummyHead.next;
}
}
进
行
一
个
分
隔
19.删除链表的倒数第N个节点
class Solution {
//考虑双临时节点情况
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode firstNode = dummyHead;
ListNode nextNode = head;
int num = 0;
while(nextNode != null){
num++;
nextNode = nextNode.next;
}
nextNode = head;
for(int i = 1;i < num-n+1;i++){
firstNode = nextNode;
nextNode = nextNode.next;
}
firstNode.next = nextNode.next;
return dummyHead.next;
}
}
进
行
一
个
分
隔
02.07. 链表相交 同160.链表相交
public class Solution {
//注意交点不是数值相等,而是指针相等
/*自己写的感觉不太彳亍,全是循环,主要思路是如果相交的话,后面长度是一样的,那么有差距的就是前面
求出长度差,长的部分从长度差后开始找,找到了地址相同就是相交,找不到就是没交点。*/
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode nodeA = headA;
ListNode nodeB = headB;
ListNode interSect = null;//存放交点
int numA = 0,numB = 0;
int a = 0;
/* 求长度*/
while(nodeA!=null){
numA++;
nodeA = nodeA.next;
}
while(nodeB!=null){
numB++;
nodeB = nodeB.next;
}
/*把长的链表的开始搜索节点向后移动*/
nodeA = headA;
nodeB = headB;
if(numA > numB){
a = numA - numB;
while(a!=0){
nodeA = nodeA.next;
a--;
}
}
else{
a = numB - numA;
while(a!=0){
nodeB = nodeB.next;
a--;
}
}
//找交点
while(nodeA!=null && nodeB!=null){
if(nodeA == nodeB){
interSect = nodeA;
break;
}
nodeA = nodeA.next;
nodeB = nodeB.next;
}
return interSect;
}
}
进
行
一
个
分
隔
142.环形链表II
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {// 有环
ListNode index1 = fast;
ListNode index2 = head;
// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
判断是否有环
进
行
一
个
分
隔
哈希表
一般哈希表都是用来快速判断一个元素是否出现集合里
242.有效的字母异位词
class Solution {
public boolean isAnagram(String s, String t) {
int[] record = new int[26];
for (int i = 0; i < s.length(); i++) {
record[s.charAt(i) - 'a']++; // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
}
for (int i = 0; i < t.length(); i++) {
record[t.charAt(i) - 'a']--;
}
for (int count: record) {
if (count != 0) {
return false;
}
}
return true;
}
}
因为判断的是小写字母,所以设计一个26长度的哈希表就可以,字母出现一次对应位置+1,在另一个字符串出现一次就-1,最后只有整个数组是0才是正确的。
进
行
一
个
分
隔
349. 两个数组的交集
import java.util.HashSet;
import java.util.Set;
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
int[] hash = new int[1005];
Set<Integer> resSet = new HashSet<>();//为了不输出重复值使用哈希表来装
for(int num : nums1){
hash[num] = 1;
}
for (int num : nums2) { // nums2中出现话,result记录
if (hash[num] == 1) {
resSet.add(num);
}
}
return resSet.stream().mapToInt(x -> x).toArray();//将结果集合转为数组
}
}
进
行
一
个
分
隔
202.快乐数
class Solution {
//求和函数
public int getNum(int n){
int sum = 0;
int i = 0;
while(n!=0){
i = n % 10;
sum += i*i;
n = n / 10;
}
return sum;
}
/*
主要思路在于无限循环,证明一定会出现之前已经出现过的数,所以判断哈希表内是否同样的数有一次出现即可
*/
public boolean isHappy(int n) {
Set<Integer> record = new HashSet<>();
int sum = n;
while(true){
record.add(sum);
sum = getNum(sum);
if(sum ==1){
break;
}
//contains用于判断集合中是否包含某个元素
if(record.contains(sum)){
return false;
}
}
return true;
}
}
进
行
一
个
分
隔
1. 两数之和
class Solution {
/*
从头遍历一遍因为要存两个关键数据,所以使用map,在map中找不到的话,就把遍历过的数据放进map中,然后再向后遍历,
因为是往后遍历的,所以找到的那个数据下标一定是大的。
*/
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> record = new HashMap<>();
int[] underIndex = new int[2];
for(int i =0; i< nums.length; i++){
int n = target - nums[i];
if(record.containsKey(n) ){
underIndex[1] = i;
underIndex[0] = record.get(n);
return underIndex;
}
record.put(nums[i],i);
}
return underIndex;
}
}
进
行
一
个
分
隔
454.四数相加II
class Solution {
/*
主要思路是要两两计算统计,ab的和,然后再计算cd的和。
*/
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int temp;
int res = 0;
//统计两个数组中的元素之和,同时统计出现的次数,放入map
for (int i : nums1) {
for (int j : nums2) {
temp = i + j;
if (map.containsKey(temp)) {
map.put(temp, map.get(temp) + 1);
}
else {
map.put(temp, 1);
}
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
for (int i : nums3) {
for (int j : nums4) {
temp = i + j;
if (map.containsKey(0 - temp)) {
res += map.get(0 - temp);
}
}
}
return res;
}
}
进
行
一
个
分
隔
383. 赎金信
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int[] hash = new int[26];
for(int i = 0;i < magazine.length();i++){
hash[magazine.charAt(i) - 'a']++;
}
for(int i = 0;i < ransomNote.length();i++){
hash[ransomNote.charAt(i) - 'a']--;
}
int index = 0;
for(int i = 0;i < 26;i++){
if(hash[i] < 0){
index++;
}
}
if(index > 0)
return false;
else
return true;
}
}
进
行
一
个
分
隔
15.三数之和
class Solution {
//a = nums[i],b = nums[left],c = nums[right]。
//排序的好处:更方便的去重,可以直接考虑上一个出现过的数字。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int i = 0;i < nums.length;i++){
if(nums[i] > 0)
return result;
if (i > 0 && nums[i] == nums[i - 1]) { // 去重a
continue;
}
int left = i + 1;
int right = nums.length - 1;
while(right > left){
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
int sum = nums[i] + nums[right] + nums[left];
if(sum > 0){
right--;
}
else if(sum < 0){
left++;
}
else{
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
//去重b c
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;/*可写可不写,因为
已经对数组进行了排序,只对一边进行去重就足以让sum不等于0了*/
right--;
left++;
}
}
}
return result;
}
}
进
行
一
个
分
隔
18.四数之和
class Solution {
//主要思路三数之和相同,外层再加一个for循环,然后在新的循环内判断剪枝(到什么时候就该停止,后面必不可能存在)去重。
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for(int k = 0;k < nums.length;k++){
if(nums[k] >= 0 && nums[k] > target)
break;
if(k > 0 && nums[k] == nums[k-1])
continue;
for(int i = k+1;i < nums.length; i++){
if(nums[i] + nums[k] > target && nums[i] + nums[k] >=0)
break;
if(i > k+1 && nums[i] ==nums[i-1])
continue;
int left = i + 1;
int right = nums.length - 1;
while(right > left){
int sum = nums[k] + nums[i] + nums[left] + nums[right];
if(sum > target)
right--;
else if(sum < target)
left++;
else{
result.add(Arrays.asList(nums[k],nums[i],nums[left],nums[right]));
while(right > left && nums[left] == nums[left+1]) left++;
while(right > left && nums[right] == nums[right-1]) right--;
right--;
left++;
}
}
}
}
return result;
}
}
进
行
一
个
分
隔
字符串
344.反转字符串
class Solution {
//双指针
public void reverseString(char[] s) {
char temp;
int left = 0;
int right = s.length - 1;
while(right > left){
temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
}
进
行
一
个
分
隔
541. 反转字符串II
//先转成字符串,设计一个移动指针,来完成问题。
class Solution {
public char[] reverseChar(char[] s, int left ,int right) {
char temp;
while(right > left){
temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
return s;
}
public String reverseStr(String s, int k) {
char[] schar = s.toCharArray();
if(s.length() < k){
schar = reverseChar(schar,0,s.length()-1);
return new String(schar);
}
else{
int j = 1;
int sum = s.length() - j;
while(true){
if((j % (2*k)) == 0){
schar = reverseChar(schar,j - (2*k),j - k -1);
sum = s.length() - j;
//一定要在反转字符串后再判断长度问题,如果再外面判断会出现j正在下一个2k区间内移动就给退出。
if(sum < (2*k))
break;
}
j++;
}
if(sum < (2*k) && sum >= k){
schar = reverseChar(schar,j,j + k -1);
}
else if(sum < k){
schar = reverseChar(schar,j,s.length()-1);
}
}
return new String(schar);
}
}
//题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转
//这个更好
class Solution {
public String reverseStr(String s, int k) {
char[] ch = s.toCharArray();
for(int i = 0; i < ch.length; i += 2 * k){
int start = i;
//这里是判断尾数够不够k个来取决end指针的位置
int end = Math.min(ch.length - 1, start + k - 1);
while(start < end){
char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;
start++;
end--;
}
}
return new String(ch);
}
}
进
行
一
个
分
隔
剑指Offer 05.替换空格
class Solution {
//逐个替换方法
public static String replaceSpace(String s) {
if (s == null) {
return null;
}
//选用 StringBuilder 单线程使用,比较快,选不选都行
StringBuilder sb = new StringBuilder();
//使用 sb 逐个复制 s ,碰到空格则替换,否则直接复制
for (int i = 0; i < s.length(); i++) {
//s.charAt(i) 为 char 类型,为了比较需要将其转为和 " " 相同的字符串类型
//if (" ".equals(String.valueOf(s.charAt(i)))){}
if (s.charAt(i) == ' ') {
sb.append("%20");
} else {
sb.append(s.charAt(i));
}
}
return sb.toString();
}
}
进
行
一
个
分
隔
151.翻转字符串里的单词
class Solution {
public String reverseWords(String s) {
s = removeExtraSpaces(s.toCharArray());
char[] sb = s.toCharArray();
StringBuilder result = new StringBuilder();
int j = sb.length -1;
//用一个新字符串往里面一个个填写单词(也可以用s.charAt()写,不需要在申请一个字符串数组)
for(int i = sb.length - 1;i >=0;i--){
if(sb[i] ==' '){
int k = i + 1;
while(k <= j){
result.append(sb[k++]);
}
result.append(' ');
j = i-1;
}
if(i == 0){
int k = i ;
while(k <= j){
result.append(sb[k++]);
}
}
}
return result.toString();
}
public String removeExtraSpaces(char[] s) {
//去除所有空格并在相邻单词之间添加空格, 快慢指针。
int slow = 0;
for (int i = 0; i < s.length; i++) {
if(s[i] != ' '){
if(slow > 0){
//给新单词后面加上空格
s[slow++] = ' ';
}
//重新在字符串内写单词
while(i < s.length && s[i] !=' '){
s[slow++] = s[i++];
}
}
}
return new String(s,0,slow); //slow的大小即为去除多余空格后的大小。
}
}
进
行
一
个
分
隔
剑指Offer58-II.左旋转字符串
class Solution {
//简单版本
public String reverseLeftWords(String s, int n) {
int len=s.length();
StringBuilder sb=new StringBuilder();
for(int i = n;i < s.length();i++)
sb.append(s.charAt(i));
for(int i = 0;i < n;i++)
sb.append(s.charAt(i));
return sb.toString();
}
}
//解法二:空间复杂度:O(1)。用原始数组来进行反转操作
//思路为:先整个字符串反转,再反转前面的,最后反转后面 n 个
class Solution {
public String reverseLeftWords(String s, int n) {
char[] chars = s.toCharArray();
reverse(chars, 0, chars.length - 1);
reverse(chars, 0, chars.length - 1 - n);
reverse(chars, chars.length - n, chars.length - 1);
return new String(chars);
}
public void reverse(char[] chars, int left, int right) {
char temp;
while (left < right) {
temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
}
进
行
一
个
分
隔
28.找出字符串中第一个匹配项的下标
class Solution {
public int strStr(String haystack, String needle) {
if(haystack.length() == 0 || needle.length() == 0)
return -1;
int[] next = nextArray(needle);
int j = 0;
int i = 0;
while(i < haystack.length()){
while(j < needle.length() && i < haystack.length() && haystack.charAt(i) == needle.charAt(j)){
j++;
i++;
}
if(j == needle.length())
break;
else if(j!=0)
j = next[j - 1];
else if(j == 0)
i++;
}
if(j == needle.length())
return i - j;
else
return -1;
}
//next数组,通过kmp算法完成next数组的初始化,当前指针的前一个数组位置是该返回的字符串下标。
public int[] nextArray(String s){
int[] next = new int[s.length()];
int j = 0;
for(int i = 1; i < s.length();i++){
while(j > 0 && s.charAt(j) != s.charAt(i))
j = next[j-1];
if(s.charAt(j) == s.charAt(i))
j++;
next[i] = j;
}
return next;
}
}
进
行
一
个
分
隔
459.重复的子字符串
class Solution {
public boolean repeatedSubstringPattern(String s) {
int[] next = nextArray(s);
int i = s.length() - next[next.length - 1];
//不可以为0,如果是0的话i为s的长度。![请添加图片描述](https://img-blog.csdnimg.cn/9ae66d3b9ea4477d97cccc0b064888d1.png)
if(next[next.length - 1] !=0 && (s.length() % i) == 0)
return true;
return false;
}
public int[] nextArray(String s){
int[] next = new int[s.length()];
int j = 0;
for(int i = 1; i < s.length();i++){
while(j > 0 && s.charAt(j) != s.charAt(i))
j = next[j-1];
if(s.charAt(j) == s.charAt(i))
j++;
next[i] = j;
}
return next;
}
}
如何获得最小重复子串:
简单推理
进
行
一
个
分
隔
双指针法
19.删除链表的倒数第N个节点
public ListNode removeNthFromEnd(ListNode head, int n){
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode fastIndex = dummyNode;
ListNode slowIndex = dummyNode;
//只要快慢指针相差 n 个结点即可
for (int i = 0; i < n ; i++){
fastIndex = fastIndex.next;
}
while (fastIndex.next != null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
//此时 slowIndex 的位置就是待删除元素的前一个位置。
//具体情况可自己画一个链表长度为 3 的图来模拟代码来理解
slowIndex.next = slowIndex.next.next;
return dummyNode.next;
}
进
行
一
个
分
隔
栈与队列
232.用栈实现队列
//用两个栈实现
class MyQueue {
Stack<Integer> stackIn;
Stack<Integer> stackOut;
public MyQueue() {
stackIn = new Stack<>(); // 负责进栈
stackOut = new Stack<>(); // 负责出栈
}
public void push(int x) {
stackIn.push(x);
}
public int pop() {
dumpstackIn();
return stackOut.pop();
}
public int peek() {
dumpstackIn();
return stackOut.peek();
}
public boolean empty() {
return stackIn.empty() && stackOut.empty();
}
private void dumpstackIn(){
if (!stackOut.empty()) return;
while (!stackIn.empty()){
stackOut.push(stackIn.pop());
}
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
进
行
一
个
分
隔
225. 用队列实现栈
class MyStack {
//只用一个队列实现
Queue<Integer> queueAll;
public MyStack() {
queueAll = new LinkedList<>();
}
public void push(int x) {
queueAll.add(x);
reserveNum();
}
public int pop() {
return queueAll.poll();
}
public int top() {
return queueAll.peek();
}
public boolean empty() {
return queueAll.isEmpty();
}
//作用是翻转之前所有输入的数据,让最新数据保持在第一位
public void reserveNum(){
int i = 1;
while(i < queueAll.size()){
queueAll.add(queueAll.remove());
i++;
}
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
进
行
一
个
分
隔
20. 有效的括号
class Solution {
//使用双端队列
public boolean isValid(String s) {
Deque<Character> deque = new LinkedList<>();
for(int i = 0;i < s.length(); i++){
if(s.charAt(i) == '(' ) deque.push(')');
else if(s.charAt(i) == '[') deque.push(']');
else if(s.charAt(i) == '{') deque.push('}');
else if(deque.isEmpty() || deque.poll() != s.charAt(i))
return false;
}
return deque.isEmpty();
}
}
进
行
一
个
分
隔
1047. 删除字符串中的所有相邻重复项
//使用栈操作
class Solution {
public String removeDuplicates(String s) {
Stack<Character> result = new Stack<>();
int i = 0;
char ch;
while(i < s.length()){
ch = s.charAt(i);
if(result.empty() || ch != result.peek()){
result.push(ch);
}
else{
result.pop();
}
i++;
}
String str = "";
//剩余的元素即为不重复的元素
while (!result.empty()) {
str = result.pop() + str;
}
return str;
}
}
class Solution {
public String removeDuplicates(String s) {
// 将 字符串res 当做栈
// 也可以用 StringBuilder 来修改字符串,速度更快
// StringBuilder res = new StringBuilder();
StringBuffer res = new StringBuffer();
// top为 res 的长度
int top = -1;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top--
if (top >= 0 && res.charAt(top) == c) {
res.deleteCharAt(top);
top--;
// 否则,将该字符 入栈,同时top++
} else {
res.append(c);
top++;
}
}
return res.toString();
}
}
进
行
一
个
分
隔
150. 逆波兰表达式求值
//思想是使用栈,运算符前两个数字,是使用该运算符计算的。计算后再把数重新放入栈中,找到运算符就计算,重复过程。
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
int i,sum;
for(String s : tokens){
if("+".equals(s)){
i = stack.pop();
sum = stack.pop() + i;
stack.push(sum);
}
else if("-".equals(s)){
i = stack.pop();
sum = stack.pop() - i;
stack.push(sum);
}
else if("*".equals(s)){
i = stack.pop();
sum = stack.pop() * i;
stack.push(sum);
}
else if("/".equals(s)){
i = stack.pop();
sum = stack.pop() / i;
stack.push(sum);
}
else{
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}
进
行
一
个
分
隔
239. 滑动窗口最大值
class Solution {
class MyQueue{
Deque<Integer> deque = new LinkedList<>();
public void pop(int val){
//弹出元素时,比较当前要弹出的数值是否等于队列出口的数值,如果相等则弹出
//同时判断队列当前是否为空
if(!deque.isEmpty() && deque.peek() == val)
deque.poll();
}
//添加元素时,如果要添加的元素大于入口处的元素,就将入口元素弹出
//保证队列元素单调递减
//比如此时队列元素3,1,2将要入队,比1大,所以1弹出,此时队列:3,2
public void push(int val){
while(!deque.isEmpty() && val > deque.peekLast())
deque.pollLast();
deque.add(val);
}
//队列队顶元素始终为最大值
public int peek() {
return deque.peek();
}
}
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums.length == 1) {
return nums;
}
//计算需要多少空间。
int len = nums.length - k + 1;
int[] res = new int[len];
int num = 0;
MyQueue result = new MyQueue();
for(int i = 0;i < k; i++)
result.push(nums[i]);
//必须现在外面存一下,然后循环内一定要放在最后,不然会漏一次结果。
res[num++] = result.peek();
for(int i = k;i < nums.length;i++){
result.pop(nums[i - k]);
result.push(nums[i]);
res[num++] = result.peek();
}
return res;
}
}
进
行
一
个
分
隔
347.前 K 个高频元素
//基于小顶堆实现,只维护k个数据。
class Solution {
//key为数组元素值,val为对应出现次数
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
for(int i:nums ){
if(map.get(i) == null) map.put(i,1);
else{
map.put(i,map.get(i) + 1);
}
}
//在优先队列中存储二元组(num,cnt),cnt表示元素值num在数组中的出现次数
//出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
for(Map.Entry<Integer,Integer> entry1 : map.entrySet()){
if(pq.size() < k)
pq.add(new int[]{entry1.getKey(),entry1.getValue()});
else if(entry1.getValue() > pq.peek()[1]){
pq.poll();
pq.add(new int[]{entry1.getKey(),entry1.getValue()});
}
}
int[] ans = new int[k];
for(int i=k-1;i>=0;i--){
ans[i] = pq.poll()[0];
}
return ans;
}
}
进
行
一
个
分
隔
二叉树
二叉树的递归遍历
144. 二叉树的前序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
traversal(root,result);
return result;
}
public void traversal(TreeNode root,List<Integer> result){
if(root == null){
return;
}
result.add(root.val);
traversal(root.left,result);
traversal(root.right,result);
}
}
145.二叉树的后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
traversal(root,result);
return result;
}
public void traversal(TreeNode root,List<Integer> result){
if(root == null){
return;
}
traversal(root.left,result);
traversal(root.right,result);
result.add(root.val);
}
}
94.二叉树的中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
traversal(root,result);
return result;
}
public void traversal(TreeNode root,List<Integer> result){
if(root == null){
return;
}
traversal(root.left,result);
result.add(root.val);
traversal(root.right,result);
}
}
进
行
一
个
分
隔
二叉树的迭代遍历
//非递归遍历,因为用的是栈的方式,所以先把右节点装进去,这样才能先把左节点放出来。前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> nodeStack = new Stack<>();
nodeStack.push(root);
while(!nodeStack.empty()){
TreeNode node = nodeStack.pop();
if(node == null) continue;
result.add(node.val);
nodeStack.push(node.right);
nodeStack.push(node.left);
}
return result;
}
}
//迭代中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
Stack<TreeNode> nodeStack = new Stack<>();
TreeNode node = root;
while(node != null || !nodeStack.empty()){
while(node != null) {
nodeStack.push(node);
node = node.left;
}
node = nodeStack.pop();
if(node == null) continue;
result.add(node.val);
node = node.right;
}
return result;
}
}
//后续迭代遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.empty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.left != null){
stack.push(node.left);
}
if (node.right != null){
stack.push(node.right);
}
}
Collections.reverse(result);
return result;
}
}
先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:
进
行
一
个
分
隔
二叉树的层序遍历
102. 二叉树的层序遍历
//通过队列进行迭代遍历
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
Queue<TreeNode> que = new LinkedList<TreeNode>();
if(root != null) que.add(root);
TreeNode temp;
while(!que.isEmpty()){
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
while(len-- > 0){
temp = que.poll();
itemList.add(temp.val);
if(temp.left != null) que.add(temp.left);
if(temp.right != null) que.add(temp.right);
}
result.add(itemList);
}
return result;
}
}
class Solution {
public List<List<Integer>> resList = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
checkFun01(root,0);
return resList;
}
//DFS--递归方式
public void checkFun01(TreeNode node, Integer deep) {
if (node == null) return;
if (resList.size() == deep) {
//当层级增加时,list的Item也增加,利用list的索引值进行层级界定
List<Integer> item = new ArrayList<Integer>();
resList.add(item);
}
resList.get(deep).add(node.val);
checkFun01(node.left, deep + 1);
checkFun01(node.right, deep + 1);
}
}
进
行
一
个
分
隔
107. 二叉树的层序遍历 II
//102的基础上反转一下就可以了。
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
Queue<TreeNode> que = new LinkedList<TreeNode>();
if(root != null) que.add(root);
TreeNode temp;
while(!que.isEmpty()){
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
while(len-- > 0){
temp = que.poll();
itemList.add(temp.val);
if(temp.left != null) que.add(temp.left);
if(temp.right != null) que.add(temp.right);
}
result.add(itemList);
}
List<List<Integer>> list = new ArrayList<>();
for (int i = result.size() - 1; i >= 0; i-- ) {
list.add(result.get(i));
}
return list;
}
}
进
行
一
个
分
隔
199.二叉树的右视图
//层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。
class Solution {
public List<Integer> rightSideView(TreeNode root) {
Queue<TreeNode> que = new LinkedList<TreeNode>();
List<Integer> result = new ArrayList<Integer>();
if(root != null) que.add(root);
TreeNode temp;
while(!que.isEmpty()){
int len = que.size();
for (int i = 0; i < len; i++){
temp = que.poll();
if(i == len -1 ) result.add(temp.val);
if(temp.left != null) que.add(temp.left);
if(temp.right != null) que.add(temp.right);
}
}
return result;
}
}
进
行
一
个
分
隔
637.二叉树的层平均值
//思路同上面那个类似
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
Queue<TreeNode> que = new LinkedList<TreeNode>();
List<Double> result = new ArrayList<Double>();
if(root != null) que.add(root);
TreeNode temp;
while(!que.isEmpty()){
int len = que.size();
double sum = 0;
for (int i = 0; i < len; i++){
temp = que.poll();
sum += temp.val;
if(i == len -1 ) result.add(sum/len);
if(temp.left != null) que.add(temp.left);
if(temp.right != null) que.add(temp.right);
}
}
return result;
}
}
进
行
一
个
分
隔
429.N叉树的层序遍历
//
class Solution {
public List<List<Integer>> levelOrder(Node root) {
Queue<Node> que = new LinkedList<Node>();
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(root != null) que.add(root);
Node temp;
while(!que.isEmpty()){
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
for (int i = 0; i < len; i++){
temp = que.poll();
itemList.add(temp.val);
//将指定集合中的所有元素按指定集合的迭代器(可选操作)返回的顺序追加到此列表的末尾。
que.addAll(temp.children);
}
result.add(itemList);
}
return result;
}
}
进
行
一
个
分
隔
515.在每个树行中找最大值
class Solution {
public List<Integer> largestValues(TreeNode root) {
Queue<TreeNode> que = new LinkedList<TreeNode>();
List<Integer> result = new ArrayList<Integer>();
if(root != null) que.add(root);
TreeNode temp;
while(!que.isEmpty()){
int len = que.size();
int num = Integer.MIN_VALUE;
for (int i = 0; i < len; i++){
temp = que.poll();
num = (num > temp.val)?num : temp.val;
if(temp.left != null) que.add(temp.left);
if(temp.right != null) que.add(temp.right);
}
result.add(num);
}
return result;
}
}
进
行
一
个
分
隔
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
class Solution {
public Node connect(Node root) {
Queue<Node> que = new LinkedList<Node>();
if(root != null) que.add(root);
Node temp1,temp2;
while(!que.isEmpty()){
int len = que.size();
while(len-- > 0){
temp1 = que.poll();
temp2 = que.peek();
if(len >= 1){
temp1.next = temp2;
}
if(temp1.left != null) que.add(temp1.left);
if(temp1.right != null) que.add(temp1.right);
}
}
return root;
}
}
进
行
一
个
分
隔
104.二叉树的最大深度
//迭代
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> que = new LinkedList<TreeNode>();
if(root != null) que.add(root);
int deep = 0;
TreeNode temp;
while(!que.isEmpty()){
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
if(len > 0) deep++;
while(len-- > 0){
temp = que.poll();
itemList.add(temp.val);
if(temp.left != null) que.add(temp.left);
if(temp.right != null) que.add(temp.right);
}
}
return deep;
}
}
//递归
class Solution {
int deep = 0;
public int maxDepth(TreeNode root){
if(root == null)
return 0;
int left = maxDepth(root.left);
int right = maxDepth(root.right);
deep = Math.max(left, right)+1;
return deep;
}
}
进
行
一
个
分
隔
111. 二叉树的最小深度
//递归方法
class Solution {
int deep = 0;
public int minDepth(TreeNode root) {
if(root == null) return 0;
int left = minDepth(root.left);
int right = minDepth(root.right);
if(root.left == null) deep = right + 1;
else if(root.right == null) deep = left + 1;
else deep = Math.min(left, right)+1;
return deep;
}
}
//迭代法
class Solution {
public int minDepth(TreeNode root) {
int deep = 0;
Queue<TreeNode> queue = new LinkedList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()){
int len = queue.size();
deep++;
while(len-- > 0 ){
TreeNode temp = queue.poll();
if( temp.left == null && temp.right == null ) return deep;
if(temp.left != null) queue.add(temp.left);
if(temp.right != null) queue.add(temp.right);
}
}
return deep;
}
}
进
行
一
个
分
隔
226. 翻转二叉树
//递归
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root != null) {
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
}
return root;
}
}
进
行
一
个
分
隔
101. 对称二叉树
//迭代的方法,使用两个队列,左右队列分别装根的左右树,并且右队列是从右节点往左节点装数据。
class Solution {
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> leftQue = new LinkedList<TreeNode>();
Queue<TreeNode> rightQue = new LinkedList<TreeNode>();
if(root.left == null && root.right == null) return true;
//System.out.println(root.left.val);
leftQue.add(root.left);
rightQue.add(root.right);
TreeNode leftTemp,rightTemp;
while(!leftQue.isEmpty() && !rightQue.isEmpty()){
int len1 = leftQue.size();
int len2 = rightQue.size();
if(len1 != len2) return false;
while(len1-- > 0){
leftTemp = leftQue.poll();
rightTemp = rightQue.poll();
if(leftTemp == null && rightTemp == null) continue;
else if(leftTemp == null || rightTemp == null ) return false;
else if(leftTemp.val == rightTemp.val) {
leftQue.add(leftTemp.left);
leftQue.add(leftTemp.right);
rightQue.add(rightTemp.right);
rightQue.add(rightTemp.left);
}
else return false;
}
}
return true;
}
}
//递归
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root.left == null && root.right == null) return true;
return match(root.left, root.right);
}
public boolean match(TreeNode left,TreeNode right){
if(left == null && right == null) return true;
if(left == null || right == null) return false;
if(left.val == right.val){
boolean a = match(left.left,right.right);
boolean b = match(left.right,right.left);
if(a == true && b == true) return true;
}
return false;
}
}
进
行
一
个
分
隔
222.完全二叉树的节点个数
//迭代
class Solution {
public int countNodes(TreeNode root) {
int result = 0;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
if(root != null) queue.add(root);
while(!queue.isEmpty()){
int len = queue.size();
result += len;
while(len-- > 0 ){
TreeNode temp = queue.poll();
if(temp.left != null) queue.add(temp.left);
if(temp.right != null) queue.add(temp.right);
}
}
return result;
}
}
/递归
class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
进
行
一
个
分
隔
110.平衡二叉树
//疯狂的递归。
class Solution {
boolean a = true;
boolean b = true;
public int high(TreeNode root){
if(root == null) return 0;
int left = high(root.left);
int right = high(root.right);
if(left > right){
if(Math.abs(left - right) > 1)
a = false;
}
else{
if(Math.abs(left - right) > 1)
b = false;
}
return Math.max(left,right) + 1;
}
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
high(root);
if(a == true && b == true)
return true;
return false;
}
}
进
行
一
个
分
隔
257. 二叉树的所有路径
//递归,使用前序遍历的递归方式(因为寻找的路径是从根节点到叶子节点,首先要遍历根结点)。
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
List<Integer> paths = new ArrayList<>();//存放节点值
traversal(root,paths,res);
return res;
}
private void traversal(TreeNode root, List<Integer> paths, List<String> res){
paths.add(root.val);//中
//找到叶节点,输出。
if(root.left == null && root.right == null){
StringBuilder sb = new StringBuilder();
for(int i = 0;i < paths.size() - 1;i++){
sb.append(paths.get(i)).append("->");
}
sb.append(paths.get(paths.size()-1));//添加最后一个节点值
res.add(sb.toString());
return ;
}
if(root.left != null) {
traversal(root.left,paths,res);
paths.remove(paths.size() - 1);//回溯(说白了就是给前一个加进去的节点去掉)
}
if(root.right !=null) {
traversal(root.right,paths,res);
paths.remove(paths.size() - 1);
}
}
}
class Solution {
/**
* 迭代法
*/
public List<String> binaryTreePaths(TreeNode root) {
List<String> result = new ArrayList<>();
if (root == null)
return result;
Stack<Object> stack = new Stack<>();
// 节点和路径同时入栈
stack.push(root);
stack.push(root.val + "");
while (!stack.isEmpty()) {
// 节点和路径同时出栈
String path = (String) stack.pop();
TreeNode node = (TreeNode) stack.pop();
// 若找到叶子节点
if (node.left == null && node.right == null) {
result.add(path);
}
//右子节点不为空
if (node.right != null) {
stack.push(node.right);
stack.push(path + "->" + node.right.val);
}
//左子节点不为空
if (node.left != null) {
stack.push(node.left);
stack.push(path + "->" + node.left.val);
}
}
return result;
}
}