给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
暴力算法
C
int removeElement(int* nums, int numsSize, int val) {
int i, j;
int len = numsSize;
for (i = 0; i < len; i++) {
if (nums[i] == val) {
j = i+1;
while (j < len) {
nums[j-1] = nums[j];//j-1是防止下标越界
j++;
}
len--;
i--;//len--的情况下i一定要--
}
}
return len;
}
/*
时间复杂度:O(n^2)
空间复杂度:O(1)
*/
public int removeElement(int[] nums, int val) {
int n = nums.length;
for(int i=0;i<n;i++){
if(nums[i]==val){
for(int j=i+1;j<n;j++){
nums[j-1] = nums[j]; // 小心越界
}
i--;// 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
n--;
}
}
return n;
}
int removeElement(int* nums, int numsSize, int val){
int solw = 0,fast;
for(fast=0;fast<numsSize;fast++){
if(val!=nums[fast]){
nums[solw++] = nums[fast];
}
}
return solw;
}
// 时间复杂度:O(n)
// 空间复杂度:O(1)
public int removeElement2(int[] nums, int val) {
int slowIndex = 0;
for(int fastIndex = 0; fastIndex< nums.length;fastIndex++){
if(val != nums[fastIndex]){
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
int searchInsert(int* nums, int numsSize, int target){
int low = 0, high = numsSize-1;
int mid;
while (low <= high) {
mid = low + (high - low) / 2;
if (target > nums[mid])
low = mid + 1;
else if (target < nums[mid])
high = mid - 1;
else
return mid;
}
return high+1;
}
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0,right = n -1; // 定义target在左闭右闭的区间里,[left, right]
while(left<=right) { // 当left==right,区间[left, right]依然有效
int mid = left + ((right - left) / 2); // 防止溢出,等价(left+right)/2
if (target < nums[mid]) {
right = mid - 1;// target 在左区间,所以[left, middle - 1]
} else if (target > nums[mid]) {
left = mid + 1;
} else { // nums[middle] == target
return mid;
}
}
return right+1;
/*
分别处理如下四种情况
1.目标值在数组所有元素之前 [0, -1]
target = -1,nums = [1,3,5,7]
第一次循环
right = 3,left = 0,mid = 1
target < nums[1] = 3 --> right = mid -1 = 0
第二次循环
right = 0,left = 0,mid = 0
target < nums[0] = 1 --> right = mid -1 = -1
循环结束,返回 right+1 = 0
2.目标值等于数组中某一个元素 return middle;
3.目标值在数组中间 [left, right],return right + 1
target = 4,nums = [1,3,5,7]
第一次循环
right = 3,left = 0,mid = 1
target > nums[1] = 3 --> left = mid + 1 = 1
第二次循环
right = 3,left = 1,mid = 2
target < nums[2] = 5 --> right = mid -1 = 1
循环结束,返回 right+1 = 2
4.目标值在数组所有元素之后的情况 [left, right], return right + 1
*/
}
int minSubArrayLen(int target, int* nums, int numsSize){
int i, j;
int sum;
int subLen;//用来标识新长度
int result = INT_MAX;//用来标识老长度,这里要取一个最大值
for (i = 0; i < numsSize; i++) {
sum = 0;
for (j = i; j < numsSize; j++) {
sum += nums[j];
if (sum >= target) {
subLen = j - i + 1;//获取该sum包含的子数组长度
result = result > subLen ? subLen : result;//判断老长度是否大于新长度
break;
}
}
}
return result==INT_MAX ? 0:result;//防止全部元素之和都不大于target
}
//暴力解法
//时间复杂度:O(n^2)
//空间复杂度:O(1)
public int minSubArrayLen(int target, int[] nums) {
int result = Integer.MAX_VALUE; // 取一个数组不可能的超过的最大值
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for(int i=0;i<nums.length;i++){
sum = 0;
for(int j=i;j<nums.length;j++){
sum+=nums[j];
if(sum>=target){
subLength = j-i+1; // 取子序列的长度
result = result<subLength?result:subLength; // 与前一次长度作比较
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == Integer.MAX_VALUE ? 0:result;
}
//滑动窗口
/*
滑动窗口的精妙之处在于根据当前子序列和大小的情况,
不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
*/
public int minSubArrayLen_2(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++){
sum+=nums[j];
//使用while,每次更新i(起始位置),并不断比较子序列是否符合条件
while(sum>=target){
subLength = (j - i + 1);//取子序列的长度
result = result < subLength? result:subLength;
sum -= nums[i++];//减去第一个数,窗口内数字个数-1,i滑动到i++位置
}
}
return result == Integer.MAX_VALUE ? 0 : result;
/*
nums[] = 2 , 3 , 1 , 2 , 4 , 3 target = 7
下标: 0 1 2 3 4 5
sum = 0;i = 0;subLength = 0;
1. ij(指向下标0) --> sum = 0+2 = 2
2. i j --> sum = 2+3 = 5
3. i j --> sum = 5+1 = 6
4. i j --> sum = 6+2 = 8
while-->subLength = j-i+1 = 3-0+1 = 4
-->result = Max < 4 ? Max:4 = 4
-->sum -= nums[i++] = sum - nums[0] = 8-2 = 6
--> i(向前移动i,即窗口缩小)
...
...
*/
}
int minSubArrayLen(int target, int* nums, int numsSize){
int i = 0,j;
int result = INT_MAX;
int sublen = 0;
int sum = 0;
for(j=0;j<numsSize;j++){
sum += nums[j];
while(sum>=target){
sublen = j-i+1;
result = result > sublen ? sublen:result;
sum -= nums[i++];
}
}
return result==INT_MAX?0:result;
}
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
分析:
JAVA
public int[][] generateMatrix(int n) {
int[][] nums = new int[n][n];
int startX = 0,startY = 0;//定义每循环一个圈的起始位置,如(0,0),(1,1)...
int loop = n / 2;//每个圈循环几次,如n=3为奇数,则loop=1只循环一圈,
//矩阵中间的值需要单独处理
int mid = n / 2;//矩阵中间的位置,如n=3,中间位置就是(1,1),n=5-->(2,2)
int count = 1;//给矩阵赋值的变量
int offset = 1;//每一圈循环,需要控制每一条边遍历的长度
int i,j;
while(loop--!=0){
i = startX;
j = startY;
//下面开始的四个for就是模拟转一圈
/*
→ → ↓
↑ → ↓
↑ ← ←
*/
//上行从左到右
for(j = startY;j<startY+n-offset;j++){
nums[startX][j] = count++;
}
//右列从上到下
for(i = startX;i<startX+n-offset;i++){
nums[i][j] = count++;
}
//模拟下行从右到左
for(;j>startY;j--){
nums[i][j] = count++;
}
//模拟左列从下到上
for(;i>startX;i--){
nums[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1,
//例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
/*
如n=4时:
第一圈:
0 1 2 3
0 → → → ↓
1 ↑ ↓
2 ↑ ↓
3 ↑ ← ← ←
-->则第一圈从(0,0)开始
第二圈:
1 2
1 → ↓
2 ↑ ←
-->则第二圈从(1,1)开始
*/
startX++;
startY++;
// offset 控制每一圈里每一条边遍历的长度
// 如n=4,第一圈第一行长度=4,第二圈第一行长度为4-2=2
/*
第一圈:
→ → → ↓
↑ ↓
↑ ↓
↑ ← ← ←
第二圈:
→ ↓
↑ ←
*/
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
/*
如n=3,中心点则为(1,1)
n=5,中心点则为(2,2)
*/
if (n % 2==1) {
nums[mid][mid] = count;
}
return nums;
}
int cmp(const void* _a, const void* _b) {
int a = *(int*)_a, b = *(int*)_b;
return a - b;
}
bool containsDuplicate(int* nums, int numsSize) {
qsort(nums, numsSize, sizeof(int), cmp);//快速排序
for (int i = 0; i < numsSize - 1; i++) {
if (nums[i] == nums[i + 1]) {
return true;
}
}
return false;
}
int maxSubArray(int* nums, int numsSize) {
int preSum = 0;//之前和
int maxSum = nums[0];//最大和
for (int i = 0; i < numsSize; i++) {
preSum = fmax(preSum + nums[i], nums[i]);//取最大值函数
maxSum = fmax(preSum, maxSum);
}
return maxSum;
}
public ListNode detectCycle(ListNode head) {
ListNode fast = head;//定义快慢指针
ListNode slow = 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 index2; //返回入口
}
}
return null;
}
给你一个链表的头节点 head 和一个整数 val
请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点 。
迭代
/*
因为可能会遇到头结点满足条件时,要删除头结点
如:val = 2
head(2)-->1-->4-->null
==>
dummyHead-->head(2)-->1-->4-->null
则只需将虚拟结点dummyHead指向head.next即可
==>
dummyHead head(2)-->1-->4-->null
| ↑
|_____________|
*/
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode();//设置一个虚拟头结点
dummyHead.next = head;//将虚拟结点指向头结点
ListNode cur = dummyHead;
while(cur.next!=null){
if(cur.next.val==val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
//当然,返回的最终链表是从虚拟结点的下一个位置开始
return dummyHead.next;
}
public ListNode removeElements2(ListNode head,int val){
if(head==null){
return head;
}
head.next = removeElements(head.next, val);
return head.val==val?head.next : head;//当条件为真时,返回head.next后的链表
}
/*
如:
null 1----->2----->3----->4
↑ ↑ ↑
pre curr temp
1. 1<-----2 3----->4
↑ ↑ ↑
pre curr temp
2. 1<-----2<------3 4
↑ ↑ ↑
pre curr temp
3. 1<-----2<------3<------4
↑ ↑ ↑
pre curr temp
*/
public ListNode reverseList(ListNode head) {
if(head==null){
return null;
}
ListNode pre = null;
ListNode curr = head;//指向头结点
ListNode temp = null;//保存curr的后一个结点
while(curr!=null){
temp = curr.next;
curr.next = pre;
pre = curr;
curr = temp;
}
return pre;
}
//链表类
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
class MyLinkedList{
/* public static void main(String[] args) {
MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);
linkedList.get(1);
linkedList.deleteAtIndex(1);
linkedList.get(1);
}
*/
int size;
ListNode head;//虚拟头结点
public int get(int index) {
if(index>=size||index<0) return -1;//返回无效索引
ListNode curr = head;
for(int i=0;i<index+1;++i){//遍历到index所指结点
curr = curr.next;
}
return curr.val;
}
public void addAtHead(int val) {
ListNode curr = new ListNode(val);
curr.next = head.next;
head.next = curr;
size++;//长度自增
}
public void addAtTail(int val) {
ListNode curr = head.next;
ListNode addNode = new ListNode(val);
while(curr.next!=null){//遍历到链表尾部
curr = curr.next;
}
curr.next = addNode;
size++;//长度自增
}
public void addAtIndex(int index, int val) {
if(index>size)
return ;
if(index<0)
index = 0;
ListNode curr = head;
for(int i=0;i<index;i++){//遍历到index下标之前一个结点
curr = curr.next;
}
ListNode addNode = new ListNode(val);
addNode.next = curr.next;
curr.next = addNode;
size++;
}
public void deleteAtIndex(int index) {
if(index>=size||index<0){//索引无效
return ;
}
ListNode curr = head;
while(index--!=0){//遍历到待删除结点的前一个结点
curr = curr.next;
}
curr.next = curr.next.next;
size--;//长度自减
}
}
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode();
dummyHead.next = head;//设置虚拟头节点
ListNode fast = dummyHead;
ListNode slow = dummyHead;
int temp = n;
while(temp-- > 0){//fast先走n步
fast = fast.next;
}
while(fast.next!=null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0,lenB = 0;
while(curA!=null){//求链表A的长度
lenA++;
curA = curA.next;
}
while(curB!=null){//求链表B的长度
lenB++;
curB = curB.next;
}
curA = headA;//让指针重新指向头节点
curB = headB;
int gap = lenA-lenB;//长度差值
if(lenB>lenA){//若链表b长度较大
curB = headA;
curA = headB;
gap = lenB - lenA;
}
while(gap-->0){//让curA(长度较大)遍历到,与curB(长度较小)同一起点上(末尾位置对齐)
curA = curA.next;
}
while(curA!=null){
if(curA==curB){//节点相同
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
/*
如:
nums = [1,2,3,4] target = 5
1. nums[i] = nums[0] = 1 target-nums[0] = 5-1 = 4(无)
map.put(nums[0],0)--> map = {{1,0}}
2. nums[i] = nums[1] = 2 target-nums[1] = 5-2 = 3(无)
map.put(nums[1],1)--> map = {{1,0},{2,1}}
3. nums[i] = nums[2] = 3 target-nums[2] = 5-3 = 2(有)
return new int[]{map.get(2),2} = {1,2}
*/
public int[] twoSum(int nums[],int target){
//map的key是值,value是下标
Map<Integer,Integer> map =
new HashMap<Integer,Integer>();
for(int i=0;i<nums.length;i++){
if(map.containsKey(target-nums[i])){
//返回一个下标数组
return new int[]{map.get(target-nums[i]),i};
}
map.put(nums[i],i);//将符合条件的数插入表中
}
return new int[0];
}
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ls = new ArrayList<List<Integer>>();
//数组排序
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
if(nums[i]>0){
return ls;
}
//去重方法
if(i>0&&nums[i]==nums[i-1]){
continue;
}
int left = i+1;//左指针
int right = nums.length -1;//右指针
while(left<right){
if(nums[i]+nums[left]+nums[right]>0){//当数值大于零,则表示数较大
right--;
}else if(nums[i]+nums[left]+nums[right]<0){//同理,数值较小
left++;
}else{
List<Integer> result = new ArrayList<Integer>();
result.add(nums[i]);
result.add(nums[left]);
result.add(nums[right]);
ls.add(result);//添加一个三元组
while(left<right&&nums[right]==nums[right-1])
right--;
while(left<right&&nums[left]==nums[left+1])
left++;
//双指针同时靠拢
right--;
left++;
}
}
}
return ls;
}
public boolean isHappy(int n) {
Set<Integer> set = new HashSet<Integer>();
while(true){
int sum = getSum(n);//每一次都会更新sum值
if(sum==1){
return true;
}
if(set.contains(sum)){//当集合中存在重复的sum时,此数不是快乐数
return false;
}else {
set.add(sum);
}
n = sum;
}
}
//取数值各个位上的单数之和
int getSum(int n){
int sum = 0;
while(n>0){
sum += (n%10) * (n%10);//求平方和
n /= 10;
}
return sum;
}
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
分析:
代码
public boolean isAnagram(String s, String t) {
if(s.length()!=t.length()){//比较长度
return false;
}
int[] record = new int[26];//定义数组,记录串s中字符的次数
char[] arr_s = s.toCharArray();
for(int i=0;i<arr_s.length;i++){
record[arr_s[i] - 'a']++;//映射出字符出现的次数
/*
如:
arr[0] = 'b';
-->
record[arr[i] - 'a'] = record['b' - 'a'] = record[1];
record[1]++ = 1; 、
即出现b在串s中出现一次,b的下标为1,
同理,a的下标为0
*/
}
char[] arr_t = t.toCharArray();
for(int i=0;i<arr_t.length;i++){
record[arr_t[i] - 'a']--;//映射出串t中出现的字符,随之映射在record表中,并-1
}
for(int i=0;i<26;i++){
if(record[i] != 0){
//判断数组中若存在不为零的次数,则不是异位词
return false;
}
}
//record中所有元素都为零,则两遍映射相抵消,即为异位词
return true;
}
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
//将数组分别装入集合中
for(int num:nums1){
set1.add(num);
}
for(int num:nums2){
set2.add(num);
}
return getIntersection(set1,set2);
}
public int[] getIntersection(Set<Integer> set1,Set<Integer> set2){
if(set1.size()>set2.size()){//遍历集合长度小的那一个
return getIntersection(set2,set1);
}
Set<Integer> intersectionSet = new HashSet<Integer>();
for(int num:set1){//判断是否有相同数字
if(set2.contains(num)){
intersectionSet.add(num);
}
}
//将集合存入数组并返回
int[] intersection = new int[intersectionSet.size()];
int index = 0;
for(int num:intersectionSet){
intersection[index++] = num;
}
return intersection;
}
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
分析:
public boolean canConstruct(String ransomNote, String magazine) {
int[] ransomNote_record = new int[26];//默认值为0
int[] magazine_record = new int[26];
for(int i=0;i<ransomNote.length();i++){//映射字母表
ransomNote_record[ransomNote.charAt(i)-'a']++;
}
for(int j=0;j<magazine.length();j++){
magazine_record[magazine.charAt(j)-'a']++;
}
for(int i=0;i<ransomNote_record.length;i++){//比较存入的字母次数
//如果r中字母的次数大于m对应的字母次数,则r不能由m表出
if(ransomNote_record[i]>magazine_record[i]){
return false;
}
}
return true;
}
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
分析:
/*
如:
nums1=[1,2] nums2=[2,1] nums3=[-1,1] nums4=[-2,1]
map --> {{3,2},{2,1},{4,1}} -->即3出现两次,2出现一次,4出现一次
1. 0-(-1+-2) = 3 -->true
temp += 2 = 2
2. 0-(-1+1) = 0 -->false
3. 0-(1-2) = 1 -->true
temp += 1 = 3
......
*/
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//定义map,key存放a+b和,value存放a+b出现的次数
Map<Integer,Integer> map =
new HashMap<Integer,Integer>();
//遍历前两个数组
for(int a:nums1){
for(int b:nums2){
//判断a+b是否出现过:若出现过则+1;反之,从0开始计数
int count =
map.containsKey(a+b) ? map.get(a+b):0;
map.put(a+b,count+1);
}
}
int temp = 0;//记录a+b+c+d=0的次数
//遍历后两个数组
for(int c:nums3){
for(int d:nums4){
if(map.containsKey(0-(c+d))){
temp += map.get(0-(c+d));//当找到a+b+c+d=0,则记录相应次数
}
}
}
return temp;
}
public boolean containsNearbyDuplicate(int[] nums, int k) {
Set<Integer> set = new HashSet<Integer>();
for(int i=0;i<nums.length;i++){
if(set.contains(nums[i])){//存在重复元素
return true;
}
set.add(nums[i]);//添加元素
if(set.size()>k){//滑动窗口个数>k
set.remove(nums[i-k]);//删除元素,i-k为当前第一个元素
}
}
return false;
}
给你一个字符串 s ,逐个翻转字符串中的所有 单词 。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
翻转后单词间应当仅用一个空格分隔。
翻转后的字符串中不应包含额外的空格。
法一:利用现有方法
public String reverseWords(String s) {
//去除开头和末尾的空白字符
s = s.trim();
//正则匹配连续的空白字符作为分隔符切割
List<String> wordList = Arrays.asList(s.split("\\s+"));
Collections.reverse(wordList);
//按照指定分隔符重组字符串
return String.join(" ",wordList);
}
//利用自定义方法
public String reverseWords_2(String s) {
StringBuilder sb = trimSpaces(s);
// 翻转字符串
reverse(sb, 0, sb.length() - 1);
// 翻转每个单词
reverseEachWord(sb);
return sb.toString();
}
//去除空格
public StringBuilder trimSpaces(String s){
int left = 0,right = s.length()-1;
// 去除首部空格
while(left <= right && s.charAt(left)==' '){
++left;//左指针往右边靠拢
}
//去掉尾部空格
while(left<=right && s.charAt(right)==' '){
--right;//右指针往左边靠拢
}
//去除多余的空白字符
StringBuilder sb = new StringBuilder();
while(left <= right){
char c = s.charAt(left);
if(c != ' '){
sb.append(c);
}else if(sb.charAt(sb.length()-1)!=' '){
sb.append(c);
}
++left;
}
return sb;
}
//反转
public void reverse(StringBuilder sb,int left,int right){
while(left < right){
char temp = sb.charAt(left);
sb.setCharAt(left++,sb.charAt(right));//首尾互换
sb.setCharAt(right--,temp);
}
}
//反转每个字母
public void reverseEachWord(StringBuilder sb){
int n = sb.length();
int start = 0,end = 0;
while (start < n) {
// 循环至单词的末尾
while (end < n && sb.charAt(end) != ' ') {
++end;
}
// 翻转单词
reverse(sb, start, end - 1);
// 更新start,去找下一个单词
start = end + 1;
++end;
}
}
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
public void reverseString(char[] s) {
for(int i=0,j=s.length-1;i<j;i++,j--){
char temp = s[i];
s[i] = s[j];
s[j] = temp;
}
System.out.println(s);
}
给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。
示例:
输入: s = “abcdefg”, k = 2
输出: “bacdfeg”
分析:
public String reverseStr(String s, int k) {
for(int i=0;i<s.length();i+=(2*k)){
//1.每隔2k个字符,对前k个字符进行反转
//2.剩余字符<2k但>=k,则反转前k个字符
if(i+k<=s.length()){
s = reverse(s,i,i+k-1);
continue;
}
//3.剩余字符
s = reverse(s,i,s.length()-1);
}
return s;
}
//反转下标为a到b的字符
public String reverse(String s,int start,int end){
char[] ch = s.toCharArray();
for(int i = start,j = end;i<j;i++,j--){
char temp = ch[i];
ch[i] = ch[j];
ch[j] = temp;
}
return new String(ch);
}
public String replaceSpace(String s) {
//建立字符数组,确保能装入替换后的容量
char[] ch = new char[s.length()*3];
int size = 0;//计算替换后的字符串长度
for(int i=0;i<s.length();i++){
char c = s.charAt(i);
if(c == ' '){//当判断为空格时,依次存入
ch[size++] = '%';
ch[size++] = '2';
ch[size++] = '0';
}else{//反之,正常赋值
ch[size++] = c;
}
}
//重新初始化新的字符串,该字符串长度为替换后的长度
String result = new String(ch,0,size);
return result;
}
public String reverseLeftWords(String s, int n) {
int len = s.length();
char[] arr = new char[s.length()];
//赋值下标为k之前的字符
for(int i=s.length()-n;i<len;i++){
arr[i] = s.charAt(i-len+n);
}
//赋值下标为k以及k后面的字符
for(int i=n;i<len;i++){
arr[i-n] = s.charAt(i);
}
String result = new String(arr,0,len);
return result;
}
实现 strStr() 函数。给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
JAVA
public int strStr(String haystack, String needle) {
if(needle==null){
return -1;
}
int i=0,j = 0,k=0;
while(i<haystack.length() && j<needle.length()){
if(haystack.charAt(i) == needle.charAt(j)){
i++;
j++;
}else{
j=0;
i = ++k;
}
}
if(j>=needle.length()){
return k;
}else{
return -1;
}
}
public class MyQueue {
Stack<Integer> stIn;//用于输入元素的栈
Stack<Integer> stOut;//用于输出元素的栈
//初始化
public MyQueue() {
stIn = new Stack<Integer>();
stOut = new Stack<Integer>();
}
//模拟入队
public void push(int x) {
stIn.push(x);//进栈
}
//模拟出队
public int pop() {
// 当stOut输出栈为空时,才让stIn中的元素输入
if(stOut.empty()){
while(!stIn.empty()){
stOut.push(stIn.pop());
}
}
return stOut.pop();//返回栈顶元素,并删除
}
//返回队列头元素
public int peek() {
//与模拟出队同理
if(stOut.empty()){
while(!stIn.empty()){
stOut.push(stIn.pop());
}
}
return stOut.peek();//返回栈顶元素,但不删除
}
//判断队列是否为空
public boolean empty() {
//当两个栈都为空时,则队列为空
return stIn.empty() && stOut.empty();
}
}
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
分析:
//252.用队列实现栈
public class MyStack {
Deque<Integer> dq;
/** Initialize your data structure here. */
public MyStack() {
dq = new LinkedList<Integer>();
}
/** Push element x onto stack. */
public void push(int x) {
int size = dq.size();
dq.offer(x);//插入队尾
for(int i=0;i<size;i++){
dq.offer(dq.poll());//poll返回最先进入的那个元素,即对头元素
//将这个元素插入队尾
//此方法就是将原始队列倒置,已构成先进后出的原则
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return dq.poll();//返回队头元素
}
/** Get the top element. */
public int top() {
return dq.peek();//返回第一个元素,即当前队头元素
}
/** Returns whether the stack is empty. */
public boolean empty() {
return dq.isEmpty();
}
}
public boolean isValid(String s) {
Stack<Character> st = new Stack<Character>();
for(int i=0;i<s.length();i++){
//这里入栈右括号是方便直接匹配串s中是否有相同的括号
if(s.charAt(i)=='(') st.push(')');
else if(s.charAt(i)=='{') st.push('}');
else if(s.charAt(i)=='[') st.push(']');
//第三种情况,栈已空,但串s未遍历完,则说明存在多余的左括号
//第二种情况,当匹配右括号时,栈中没有发现匹配的字符
else if(st.empty() || st.peek()!=s.charAt(i)) {
return false;
}
/*
隐含的判断成功:
s.charAt(i)=='(' 或
s.charAt(i)=='{' 或
s.charAt(i)=='['
*/
else st.pop();
}
//第一种情况:已遍历完,但栈不空,则说明存在多余右括号
return st.empty();
}
public String removeDuplicates(String s){
Stack<Character> st = new Stack<Character>();
for(int i=0;i<s.length();i++){
if(!st.isEmpty()){
//判断是否有相邻元素
if(st.peek()==s.charAt(i)){
st.pop();
continue;
}
}
st.push(s.charAt(i));//入栈
}
//栈无元素则返回空
if(st.isEmpty()){
return "";
}
char[] ch = new char[st.size()];
//反转
for(int i=ch.length-1;i>=0;i--){//注意:不能用栈的长度,因为每次出栈后会变化
ch[i] = st.pop();
}
//重造字符串
String result = new String(ch, 0, ch.length);
return result;
}
public int evalRPN(String[] tokens) {
Deque<Integer> dq = new LinkedList<Integer>();
for(int i=0;i<tokens.length;i++){
//判断是否为算术符
if(tokens[i].equals("+")||tokens[i].equals("-")
||tokens[i].equals("*")||tokens[i].equals("/")){
int a = dq.pop();
int b = dq.pop();
switch (tokens[i]){
case "+":
dq.push(b+a);
break;
case "-":
dq.push(b-a);
break;
case "*":
dq.push(b*a);
break;
case "/":
dq.push(b/a);
break;
default:
}
}else{
dq.push(Integer.parseInt(tokens[i]));//字符串转化为数字
}
}
return dq.peek();
}
void preorder(TreeNode *root,int *res,int *resSize) {
if (root != NULL) {
//用数组存放值
res[(*resSize)++] = root->val;//数组自增
preorder(root->left, res, resSize);
preorder(root->right, res, resSize);
}
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int *res = malloc(sizeof(int) * 2000);
*returnSize = 0;
preorder(root, res, returnSize);
return res;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int *result = (int*)malloc(sizeof(int) * 2000);//生成一个结果数组
*returnSize = 0;
if (root == NULL) {
return result;
}
struct TreeNode *st[2000];//定义一个栈
struct TreeNode *p = root;//定义一个指针
int top = -1;//初始化栈
st[++top] = root;//入栈根节点
while (top != -1) {
p = st[top--];//指针指向当前需遍历的结点(根左右)
result[(*returnSize)++] = p->val;//入结果数组
if (p->right != NULL) {//右孩子存在
st[++top] = p->right;
}
if (p->left != NULL) {//左孩子存在
st[++top] = p->left;
}
}
return result;
}
void inorder(struct TreeNode* root, int *res, int *resSize) {
if (root == NULL) {
return;
}
inorder(root->left, res, resSize);
res[(*resSize)++] = root->val;
inorder(root->right, res, resSize);
}
int *inorderTraversal(struct TreeNode *root, int *returnSize) {
int *res = (int*)malloc(sizeof(int) * 2000);
*returnSize = 0;
inorder(root, res, returnSize);
return res;
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int *result = (int*)malloc(sizeof(int) * 2000);//生成一个结果数组
*returnSize = 0;
if (root == NULL) {
return result;
}
struct TreeNode *st[2000];//定义一个栈
struct TreeNode *p;//定义一个指针
int top = -1;//初始化栈的指针
p = root;//p指向根节点
while (top != -1 || p != NULL) {//有可能出现栈空但树未遍历完的情况
while (p != NULL) {//左孩子一直入栈
st[++top] = p;
p = p->left;
}
if (top != -1) {//栈不空时,输出
p = st[top--];
result[(*returnSize)++] = p->val;
p = p->right;
}
}
return result;
}
void postorder(TreeNode *root, int *res, int *resSize) {
if (root == NULL) {
return;
}
postorder(root->left, res, resSize);
postorder(root->right, res, resSize);
res[(*resSize)++] = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int *res = (int*)malloc(sizeof(int) * 2000);
*returnSize = 0;
postorder(root, res, returnSize);
return res;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
int *result = (int*)malloc(sizeof(int) * 2000);//结果数组
*returnSize = 0;
if (root = NULL) {
return result;
}
struct TreeNode *stack1[2000]; int top1 = 1;
struct TreeNode *stack2[2000]; int top2 = -1;
struct TreeNode *p = NULL;
stack1[++top1] = root;//根节点入栈
while (top1 != -1) {
p = stack1[top1--];//指向
stack2[++top2] = p;//再入
if (p->left != NULL) {//反向入栈
stack1[++top1] = p->right;
}
if (p->right != NULL) {
stack1[++top1] = p->left;
}
}
while (top2 != -1) {
//再次出栈即为后序遍历
p = stack2[top2--];
result[(*returnSize)++] = p->val;
}
return result;
}
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode left = invertTree(root.right);
TreeNode right = invertTree(root.left);
root.left = left;
root.right = right;
return root;
}
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return compare(root.left,root.right);
}
public boolean compare(TreeNode left,TreeNode right){
//判断四种情况
if(left == null && right != null) return false;
else if(left != null && right == null) return false;
else if(left == null && right == null) return true;
//若左右都不为空时,判断值是否相等
else if(left.val != right.val) return false;
//当左右子树值相等时,继续递归遍历
boolean outside = compare(left.left,right.right);//左子树的外层,右子树的外层
boolean inside = compare(left.right,right.left);//左子树的内层,右子树的内层
boolean isSame = outside && inside;
return isSame;
}
bool isSymmetric(struct TreeNode* root){
bool compare();
if (root == NULL) {
return true;
}
return compare(root->left, root->right);
}
bool compare(struct TreeNode* left,struct TreeNode *right) {
if (left == NULL && right != NULL) return false;
else if (left != NULL && right == NULL) return false;
else if (left == NULL && right == NULL) return true;
else if (left->val != right->val) return false;
bool outSide = compare(left->left, right->right);
bool inSide = compare(left->right, right->left);
bool isSame = outSide && inSide;
return isSame;
}
int result;
public int maxDepth(TreeNode root){
result = 0;
if(root == null) return result;//注意细节,返回的是result
getDepth(root,1);
return result;
}
public void getDepth(TreeNode root,int depth){
//采用前序遍历
result = depth > result ? depth : result;//中
if(root.left==null && root.right==null) return;//为空
if(root.left!=null){//左
depth++;
getDepth(root.left,depth);
depth--;//因为已经走到下一层了,depth进行了+1,即回溯,所以深度-1,
}
if(root.right!=null){//右
depth++;
getDepth(root.right,depth);
depth--;//
}
return ;
}
public int minDepth(TreeNode root) {
if(root == null) return 0;
int leftDepth = minDepth(root.left);//取左
int rightDepth = minDepth(root.right);//取右
//求的是根节点到叶子节点的节点距离,所以:
//左子树为空,右不为空,这时并不是最低点
if(root.left == null && root.right!=null){
return 1+rightDepth;
}
//当一个右子树为空,左不为空,这时并不是最低点
if(root.left!=null && root.right==null){
return 1+leftDepth;
}
return 1+Math.min(leftDepth,rightDepth);
}