Leecode 高频之数组和链表

93 阅读3分钟

1. 删除排序数组中的重复项

​ 要求:给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

思路:1. 在不改变当前数组的情况下,将当前数组的下表往后移动

​ 2. 在原有的数组不变更的情况下,找到当前的不重复的值赋值给当前下标的值

public static int removeDuplicates(int[] nums) {

    if(nums == null || nums.length == 0){
        return 0;
    }

    int len = 0;
    for(int i=0;i<nums.length;){
        while (i<nums.length -1 &&nums[i] == nums[i+1] ){
            i++;
        }
        nums[len++] = nums[i++];
    }
    return len;
}

2. 旋转数组

要求:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

​ 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 要求使用空间复杂度为 O(1) 的原地算法。

方案一:一个一个替换,比较繁琐

public void rotate(int[] nums, int k){
     k = k % nums.length;
     for(int i=0;i<k;i++){
         int fir = nums[nums.length - 1];
         //从倒数第二个元素开始移动
         for (int j = nums.length - 2; j >= 0; j--) {
             nums[j + 1] = nums[j];
         }
         //每次的第一个元素都是移动前的最后一个
         nums[0] = fir;
     }
}

方案二:将旧数组的元素按照替换后的位置重新赋给新数组

public void rotateArray(int[] nums, int k) {
    k = k % nums.length;
    int[] nnums = new int[nums.length];
    for (int i = 0; i < nums.length; i++) {
        int num = (i + k) % nums.length;
        nnums[num] = nums[i];

    }
    for (int i = 0; i < nnums.length; i++) {
        nums[i] = nnums[i];
    }
}

方案三:自组旋转

public void rotate(int[] nums, int k) { if(nums==null || nums.length==0){ return ; } if(nums.length==1){ return ; } k=k%nums.length; reverse(nums); reverse(nums,0,k-1); reverse(nums,k,nums.length-1); } public void reverse(int[] arr,int begin,int end){ int half=(end-begin+1)/2; int tar; for(int i=0;i<half;i++){ tar=arr[begin]; arr[begin]=arr[end]; arr[end]=tar; begin++; end--; }

}

public void reverse(int[] arr){
    reverse(arr,0,arr.length-1);
}

3. 合并两个有序数组

要求:给定两个排序整数数组nums1nums2,将nums2合并为一个排序数组nums1nums1nums2中初始化的元素数量分别为mn。 您可以假设nums1有足够的空间(大小大于或等于m + n)来容纳nums2中的其他元素。

思路:混合插入有序数组,由于两个数组都是有序的,所以只要按顺序比较大小即可。题目中说了nums1数组有足够大的空间,说明我们不用resize数组,又给了我们m和n,那就知道了混合之后的数组大小,这样我们就从nums1和nums2数组的末尾开始一个一个比较,把较大的数,按顺序从后往前加入混合之后的数组末尾。

// 备注:当前代码编译通过不了主动扩展,因为当前的数组无法

public static int[] mergeArray(int nums1[],int m,int nums2[],int n){
    int i = m-1,j = n-1,k = m+n-1;
    while (i>=0 && j>=0){
        nums1[k--] = nums1[i]>nums2[j]?nums1[i--]:nums2[j--];
    }
    while (j>=0){
        nums1[k--] = nums2[j--];
    }

    return nums1;
}

对上面的代码进行整合:

public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m-1, j = n-1, k = m+n-1;
        while(j>=0){
            nums1[k--] = i >= 0 && nums1[i]>nums2[j] ? nums1[i--] : nums2[j--];
        }
    }

4. 两两交换链表中的结点

要求:给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

方案一思路:

  1. 申请一个空结点
  2. 使用空结点作为当前的临时替换结点
public Node swapPairs(Node head) {
    Node tmp =new Node(0);  //申请一个空的节点
    tmp.next = head;  //让链表的头节点指向那个空节点的下一个节点

    Node temp = tmp;  //把这个空节点保存下来,用这个变量去完成交换
    while(head != null && head.next !=null){
        temp.next = head.next;
        head.next = temp.next.next;
        temp.next.next = head;
        temp = temp.next.next;  //当上面交换完了后,temp向后移两个节点。
        head = temp.next;
    }
    return tmp.next; //返回空节点之后已经交换完了的链表
}

方法二:把节点一个一个取出来,两个两个一交换,用递归实现,

public Node swapPairs2(Node head) {

    if(head == null || head.next == null){
        return head;
    }

    Node res = head.next; //找到交换节点的下一个
    Node temp = swapPairs(head.next.next);  //需要移动头部
    res.next = head; //交换
    head.next = temp; //头部移动到下一组
    return res; //返回空节点之后已经交换完了的链表
}

5. 三数之和

要求:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

方法一:暴力解法

遍历选取任意三个元素相加是否为零,利用Set去重。
public List<List<Integer>> threeSum(int[] nums) {
    Set<List<Integer>> Set = new HashSet<List<Integer>>();
    Arrays.sort(nums);
    for(int i=0;i<nums.length-2;i++)
        for(int j=i+1;j<nums.length-1;j++)
            for(int k=j+1;k<nums.length;k++)
                if(nums[i]+nums[j]+nums[k]==0)Set.add(Arrays.asList(nums[i],nums[j],nums[k]);
    return new ArrayList<List<Integer>>(Set);
}

方案二: 双指针法

思路:

  1. 排序
  2. 固定一个值,找另外二个值它们和等于 0,如何找另外两个值,用的是双指针!
public List<List<Integer>> threeSum(int[] nums) {
    Arrays.sort(nums);
    int n = nums.length;
    List<List<Integer>> res = new LinkedList<>();
    for(int i=0;i<n;i++){
        if(i>0 && nums[i] == nums[i-1]){
            continue;
        }
        int left = i+1;
        int right = n-1;
        while (left < right){
            int tmp = nums[i] + nums[left] + nums[right];
            if(tmp == 0){
                res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                while (left < right && nums[left] == nums[left + 1]) left += 1;
                while (left < right && nums[right] == nums[right - 1]) right -= 1;
                left += 1;
                right -= 1;
            }else if(tmp > 0){
                right -= 1;
            }else {
                left += 1;
            }
        }
    }
    return res;
}