双指针法(数据结构与算法java代码)

第一题 删除有序数组的重复项

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

快慢指针法

class Solution {
    public int removeDuplicates(int[] nums) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != nums[slowIndex]) {
                slowIndex++;
                nums[slowIndex] = nums[fastIndex];
            }
        }
        return slowIndex + 1;
    }
}

暴力解法

双for嵌套,一层遍历数组元素,一层更新数组,时间复杂度为O(n ^ 2),不考虑使用


第二题 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

快慢指针法一

非零值往前覆盖,最后在后面自行补零

class Solution {
    public void moveZeroes(int[] nums) {
        int slowIndex = 0;
        int n = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != 0) {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++; 
            } else {
                n++; // 0个数
            }
        }
        while (n > 0) {
            nums[nums.length - n] = 0;
            n--;
        }
    }
}

快慢指针法二

左右指针交换位置,按顺序将非零值往前移动,零往后移动

​
class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length, left = 0, right = 0;
        while (right < n) {
            if (nums[right] != 0) {
                swap(nums, left, right);
                left++;
            }
            right++;
        }
    }

    public void swap(int[] nums, int left, int right) {
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
    }
}

第三题 比较含退格的字符串

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

自解

双层循环,第一层逆序循环更新字符串,遇到'#'进入第二层循环,通过计数器记录需要跳过的字符个数,在跳过的过程中及时退出循环,不足之处是元素的顺序倒了

class Solution {
    public boolean backspaceCompare(String s, String t) {
        if (back(s).equals(back(t))) {
            return true;
        }
        return false;
    }

    public static String back(String s) {
        StringBuilder sb = new StringBuilder();  
        int i = s.length() - 1;
        while (i >= 0) {  
            char c = s.charAt(i);  
            if (c == '#') {  
                for (int count = 2; count > 0; count--) {
                    i--;
                    if (i < 0) {
                        break;
                    }
                    if (s.charAt(i) == '#') {
                        count += 2;
                    }
                }
            } else {  
                sb.append(c); 
                i--; 
            }  
        }  
        return sb.reverse().toString();  
    }
}

官解一:栈处理

单层循环,如果遇到了退格符,那么就把栈顶元素弹出,非退格符的元素,就按顺序依次进栈

此方法虽然简单,但是实际运行时,时间效率和空间效率上都略逊于上一个答案

class Solution {
    public boolean backspaceCompare(String S, String T) {
        return build(S).equals(build(T));
    }

    public String build(String str) {
        StringBuffer ret = new StringBuffer();
        int length = str.length();
        for (int i = 0; i < length; ++i) {
            char ch = str.charAt(i);
            if (ch != '#') {
                ret.append(ch);
            } else {
                if (ret.length() > 0) {
                    ret.deleteCharAt(ret.length() - 1);
                }
            }
        }
        return ret.toString();
    }
}

官解二:双指针法

逆序遍历,两个指针分别记录遇到的第一个无需删除的字符,使用计数器记录删除次数,然后比较记录的字符是否相同,不同返回false,相同则继续遍历

class Solution {
    public boolean backspaceCompare(String S, String T) {
        int i = S.length() - 1, j = T.length() - 1;
        int skipS = 0, skipT = 0;

        while (i >= 0 || j >= 0) {
            while (i >= 0) {
                if (S.charAt(i) == '#') {
                    skipS++;
                    i--;
                } else if (skipS > 0) {
                    skipS--;
                    i--;
                } else {
                    break;
                }
            }
            while (j >= 0) {
                if (T.charAt(j) == '#') {
                    skipT++;
                    j--;
                } else if (skipT > 0) {
                    skipT--;
                    j--;
                } else {
                    break;
                }
            }
            if (i >= 0 && j >= 0) {
                if (S.charAt(i) != T.charAt(j)) {
                    return false;
                }
            } else { // 有一方遍历完毕,小于零
                if (i >= 0 || j >= 0) {
                    return false;
                }
            }
            i--;
            j--;
        }
        // 全部遍历完毕
        return true;
    }
}

第四题 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

简单做法:冒泡排序

时间效率很低

class Solution {
    public int[] sortedSquares(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            nums[i] = nums[i] * nums[i];
        }
        for (int i = 0; i < nums.length - 1; i++) {
            for (int j = 0; j < nums.length - i - 1; j++) {
                if (nums[j] > nums[j + 1]) {
                    int ret = nums[j + 1];
                    nums[j + 1] = nums[j];
                    nums[j] = ret;
                }
            }
        }
        return nums;
    }
}

简单做法:Arrays.sort方法

比冒泡稍微强一点

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 n = nums.length;
        int negative = -1;
        for (int i = 0; i < n; ++i) {
            if (nums[i] < 0) {
                negative = i;
            } else {
                break;
            }
        }

        int[] ans = new int[n];
        int index = 0, i = negative, j = negative + 1;  // 负数 i, 正数或0 j
        while (i >= 0 || j < n) {
            if (i < 0) { // 负数已经遍历完
                ans[index] = nums[j] * nums[j];
                ++j;
            } else if (j == n) { // 正数已经遍历完了,倒序添加负数平方
                ans[index] = nums[i] * nums[i];
                --i;
            } else if (nums[i] * nums[i] < nums[j] * nums[j]) {
                ans[index] = nums[i] * nums[i];
                --i;
            } else {
                ans[index] = nums[j] * nums[j];
                ++j;
            }
            ++index;
        }
        return ans;
    }
}

官解二:双指针法

和上一个解答类似,不过是将正负数为界限遍历改成了分别从头尾遍历,优点是不用处理某一指针到边界的情况

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n = nums.length;
        int[] ans = new int[n];
        for (int i = 0, j = n - 1, pos = n - 1; i <= j;) {
            if (nums[i] * nums[i] > nums[j] * nums[j]) {
                ans[pos] = nums[i] * nums[i];
                ++i;
            } else {
                ans[pos] = nums[j] * nums[j];
                --j;
            }
            --pos;
        }
        return ans;
    }
}

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值