目前刷题小结

本文详细解析了多种字符串和数组相关的算法题,包括无重复字符的最长子串、最长回文子串、实现strStr()、字符串相加、删除字符串中所有相邻重复项等。同时介绍了使用双指针、栈、哈希集合等数据结构解决此类问题的方法。此外,还涉及数组的重复数字查找、两数之和、三数之和等经典算法。
摘要由CSDN通过智能技术生成

字符串相关
3.无重复字符的最长子串
思路:不重复,先考虑HashSet。
双指针法,左指针用于从左到右一个一个的变换子串的开头,右指针在子串头确定以后一直往后遍历。右指针遍历到的如果是第一次出现就加入set;每次右指针循环退出(包括两种情况,要不是遍历完了,要不就是右重复),这时候左指针右移,右移后要把前面这个从set中remove,因为左指针管的是每次子串的头部,现在头部换了。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        HashSet<Character> set = new HashSet<>();
        int len = s.length();
        int right = 0, nums = 0;  //右指针从0开始遍历
        //这里的循环是遍历左指针
        for(int i = 0;i < len; i++){
            if(i!= 0){
                set.remove(s.charAt(i-1));  //为什么是i-1,因为一次循环完之后,i++了,要移除的是i前面的元素
            }
            while(right < len && !set.contains(s.charAt(right))){//只要集合中不包含右指针的元素,就不断的往集合添加
                set.add(s.charAt(right));
                right++;
            }
            nums = Math.max(nums,right - i);
        }
        return nums;
    }
}

5.最长回文子串
回文子串一个字符串的子串正着读反着读都一样,比如aba,就是镜像对称的。因此我们有这样的思路:从中心向外扩展,设定左右指针,一种可能是左边或右边的元素和目前指定的元素相同,如若不然就是该元素的左右元素相同。

class Solution {
    public String longestPalindrome(String s) {
        if(s == null ||s.length()<1){
            return "";
        }
        int strLen = s.length(),left = 0, right = 0, len = 1,start = 0, maxLen = 0;
        for(int i = 0; i < strLen; i++){
            left = i-1;
            right = i+1;
            while(left>=0&&s.charAt(left) == s.charAt(i)){
                left--;
                len++;
            }
            while(right<strLen&&s.charAt(right) == s.charAt(i)){
                right++;
                len++;
            }
            while(left>=0&&right<strLen&&s.charAt(right)==s.charAt(left)){
                right++;
                left--;
                len = len+2;
            }
            if(len > maxLen){
                maxLen = len;
                start = left;
            }
            len = 1;
        }
        return s.substring(start+1,start + maxLen +1);
    }
}

28.实现strStr()
要求在串1中找到串2出现在其中的第一个位置

class Solution {
    public int strStr(String haystack, String needle) {
        int n = haystack.length();
        int m = needle.length();
        for(int i = 0; i + m <= n; i++){
            boolean flag = true;
            for(int j = 0; j < m; j++){
                if(haystack.charAt(i+j)!=needle.charAt(j)){
                    flag = false;
                    break;
                }
            }
            if(flag){
                return i;
            }
        }
        return -1;
    }
}

415.字符串相加
要求元素是整数字符串的两个串相加。
思路,把每一个字符取出来,按照小学的那种竖着来加的方法一个元素一个元素的加,同时要考虑两个问题,一个是进位,一个是字符取出来之后怎么表达为10进制的问题,后一个问题的解决办法是让这个字符减去’0’字符,因为字符按照编码方法字符’2’与字符’0’相减就是十进制2,因为它们编码相差2。

class Solution {
    public String addStrings(String num1, String num2) {
        int i = num1.length()-1,j = num2.length()-1, add = 0;
        StringBuffer res = new StringBuffer();//用来往里添加元素结果,最开始确定不到大小
        while(i>=0||j>=0||add == 1){
            int x = i >=0 ? num1.charAt(i) - '0' : 0; //-'0'的作用是得到第i个字符的十进制值
            int y = j >=0 ? num2.charAt(j) - '0' : 0;
            int addRes = x + y +add;
            res.append(addRes % 10);
            add = addRes/10; //除就是要截断,不是0就是进位1
            i--;
            j--;
        }
        return res.reverse().toString();
    }
}

1047.删除字符串中所有相邻重复项
“abbaca”输出“ca”,我感觉又可以用栈做,又带了一些匹配的影子。也蒜是栈的思想吧。遍历字符串元素,添加到StringBuffer中,每添加一个,让top指针指向buffer的当前元素,如果要添加的下一个元素和这个top指向的元素相同就把它取出来。

class Solution {
    public String removeDuplicates(String s) {
        StringBuffer stack = new StringBuffer();
        int top = -1;
        for(int i = 0;i < s.length();  i++){
            char c = s.charAt(i);
            if(top>=0&&stack.charAt(top)==c){
                stack.deleteCharAt(top);
                top--;
            }
            else{
                stack.append(c);
                top++;
            }
        }
        return stack.toString();
    }
}

数组相关
剑指offer03
数组中的重复数字思路:遍历数组元素,首次出现的就扔到set里,后续遍历如果set里有这个数,那么就返回这个数的下标,说明找到了重复的数字。

public class Solution {
    public int findRepeatNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < nums.length ; i++) {
            if(set.contains(nums[i])){
                return nums[i];
            }
            else{
                set.add(nums[i]);
            }
        }
        return 0;
    }
}

1.两数之和
这个题目要求在整数数组中,找到和为target的两个数返回他们的下标。
思路:最暴力的,两次遍历,第二层遍历j从i+1开始,因为前面的已经遍历过了。那么让num[i]+num[j]=target就算找到了。这种比较简单。

public class Solution {
    public int[] twoSum(int[] nums,int target) {
        int n = nums.length;
        for (int i = 0; i < n ; i++) {
            for (int j = i+1; j < n; j++) {//两次遍历
                if(target == nums[i]+nums[j]){
                    return new int[]{i,j};
                }
            }
        }
       return new int[0];
    }
}

15.三数之和
判断整数数组中是否存在a+b+c=0
思路:想都不用想,上来先双指针法,找三个数比较困难,因此先给数组排序后,在有序的情况下也方便跳出临界条件。

class Solution{
    public List<List<Integer>> threeSum(int[] sums){
        int n = sums.length;
        Arrays.sort(sums);
      List<List<Integer>> ans = new ArrayList<List<Integer>>();
        for (int first = 0; first < n; first++) {
            if(first>0 && sums[first] == sums[first-1]){//枚举的前后两个数相同,跳出本次循环
                continue;
            }
            int third = n-1;//第三个数从最后一个开始枚举
            int target = -sums[first] ;
            int second = first + 1 ;//目标值让另外两个加起来等于它,因为三个加起来是0
            while(third > second){
                if(sums[second] + sums[third] == target){
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(sums[first]);
                    list.add(sums[second]);
                    list.add(sums[third]);
                    ans.add(list);
                    second++;
                    third--;
                    while(second<third&&sums[second]==sums[second-1]){
                        second++;
                    }
                    while(second<third&&sums[third]==sums[third+1]){
                        third--;
                    }
                }else if(sums[second] + sums[third] <target){
                    second++;

                }else{
                    third--;
                }

            }
       }

        return ans;
    }

}

链表相关
反转链表
思路:很简单,让每一个节点都指向原本它前一个节点即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while(cur!=null){
            ListNode next1 = cur.next; //应该是把cur。next保存下来
            cur.next = pre;
            pre = cur;
            cur = next1;
        }
        return pre;
    }
}

21.合并两个有序链表
思路:先创建一个虚拟的头结点,链表的感觉就是在于虚拟结点的灵活运用。
如果L1的data比L2的data小,就让虚拟节点连L1,否则就连L2,然后指向L1和L2的指针往后移动,一直比,当有一个为空了就不用比了,哪个不是空,就挂在前面新组成的链表后面,然后返回的是虚拟头节点的下一个节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode pre = new ListNode(-1);
        ListNode prehead = pre;
        while(l1!=null&&l2!=null){
            if(l1.val<=l2.val){
                pre.next = l1;
                l1 = l1.next;
            }
            else{
                pre.next = l2;
                l2 = l2.next;
            }
        pre = pre.next;
        }
        pre.next = l1==null?l2:l1;
        return prehead.next;
        }
    }

栈、队列相关
20.有效的括号
要求左右括号以相同类型以及正确的顺序闭合,例如[]{}()或者{([])}。
思路:匹配问题利用进栈和出栈来解决。遇到左括号就压入栈,当遇到右括号,根据栈的后进先出的原则,当现在是一个}的右括号,那么栈顶弹出来的一定是一个{才对,直到最后栈空说明都匹配好了。

class Solution {
    public boolean isValid(String s) {//对于栈的操作,就是出栈入栈,解决匹配问题,表达式的值,利用好出栈和入栈的顺序
            int n = s.length();
            if(n % 2 == 1){
                return false;
            }
            Stack<Character> stack = new Stack<>();
            for(int i = 0 ; i < n; i++){
                char c = s.charAt(i);
                if(c=='('||c=='['||c=='{'){
                    stack.push(c);//左括号压入栈
                }
                else{
                    if(stack.isEmpty()){
                        return false;
                    }
                    char top = stack.pop();
                    if(c ==')' && top !='('){
                        return false;
                    }
                    else if(c ==']' && top !='['){
                        return false;
                    } 
                    else if(c =='}' && top !='{'){
                        return false;
                    }
                }
            }
            return stack.isEmpty();
    }
}

剑指offer9.用两个栈实现队列
思路:栈是先入后出,队列是先入先出。入队的时候,只要栈1工作即可,那么出队,由于最先入队的要最先出来,那么需要让栈底元素转到栈顶,才能获得先出的效果,那么就让栈一的元素全部放入栈2,由栈2实现出队。

class CQueue {
    LinkedList<Integer> stack1;
    LinkedList<Integer> stack2;
    public CQueue() {
        stack1 = new LinkedList();
        stack2 = new LinkedList();
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        if(stack2.isEmpty()){ //已经把栈1的所有数扔进栈2,如果栈1一开始就是空的,呢么也不会向栈2扔数据,直接返回-1
            return -1;
        }
        else{
            int delidx = stack2.pop();  //栈2只要不空,那它一定比新加进来的栈1的数据进来的早,要删也是从栈2删,直到删完
            return delidx;
        }
    }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值