绝密笔记 | 算法(二)双指针/迭代

例题

1,反转链表(递归,双指针/迭代)

来自 LeetCode206

补充:

* 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; }
 * }

解法

1,递归

在递归文章里。

2,双指针

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = head;
        ListNode pre = null;
        ListNode temp = null;
        while(cur!=null){
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

cur指向当前节点,pre指向前一个节点,temp指向后一个节点。迭代进行。

理解(不保证正确)

1,链表题应该在本子上画出过程,这样很容易得到算法。

2,固定位置的反转链表(递归,双指针/迭代)

来自 LeetCode92

补充:

* 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; }
 * }

解法

1,双指针解法

class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {

        if(left == right)
            return head;

        ListNode cur = head;
        ListNode pre = null;
        ListNode temp = null;
        ListNode now = null;
        ListNode r = null;
        ListNode l = head;
        int count = 1;
        for(;count<left;count++){
            cur = cur.next;
        }
  
        while(count<=right&&cur!=null){
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
            count++;
        }
        for(int i =1;i<left-1;i++){
            l=l.next;
        }


        if(left!=1){
            l.next = pre;
            pre = l;
        }
        now = head;
        while(now.next!=null)
            now = now.next;
        now.next = temp;
        if(left==1)
            return pre;
        return head;
    }
}

根据反转链表1的双指针算法写出。

对前面未反转(left之前)的链表处理过于暴力。

大佬的双指针解法:

class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        // 定义一个dummyHead, 方便处理
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;

        // 初始化指针
        ListNode g = dummyHead;
        ListNode p = dummyHead.next;

        // 将指针移到相应的位置
        for(int step = 0; step < m - 1; step++) {
            g = g.next; p = p.next;
        }

        // 头插法插入节点
        for (int i = 0; i < n - m; i++) {
            ListNode removed = p.next;
            p.next = p.next.next;

            removed.next = g.next;
            g.next = removed;
        }

        return dummyHead.next;
    }
}

采用的是虚拟头指针dummyHead,这样可以避免前后边界问题

2,迭代

有空写。

理解(不保证正确)

1,链表题中出现左右边界值问题,引入一个虚拟头指针,能避免大量问题。

3,相交链表

来自 LeetCode160

解法

1,双指针的智商题

其实就是走两次,经常能用到这个思想

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode A = headA;
        ListNode B = headB;
        while(A!=B){
            if(A==null)
                A = headB;
            else
                A = A.next; 
            if(B==null)
                B = headA;
            else
                B = B.next;
        }
        return  A;
    }
}

理解

无。

3,字符串相加(大数相加)

来自LeetCode 415

解法

class Solution {
    public String addStrings(String num1, String num2) {
        StringBuffer sb = new StringBuffer();
        int add = 0;
        int i = num1.length()-1;
        int j = num2.length()-1;
        while(i>=0||j>=0||add!=0){
            int x = i >= 0? num1.charAt(i) - '0' : 0;
            int y = j >= 0? num2.charAt(j) - '0' : 0;
            int result = x + y + add;
            if(result>=10)
                add = 1;
            else 
                add = 0;
            sb.append(result%10);
            i--;
            j--;
        }
        sb.reverse();
        return sb.toString();
    }
}

理解

1,双指针指向个位数,然后用StringBuffer进行存储相加

2,边界条件很多,需要特别小心。

3,需要熟练掌握String/StringBuffer/StringBuilder。

4,合并两个有序数组

来自LeetCode88

注意:
1,需要审题,题目告诉我们是有序数组(从小到大)。

解法

1,nt解法

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m;
        for(int j = 0;j<n;i++,j++){
            nums1[i] = nums2[j];
        }
        Arrays.sort(nums1);
    }
}

丢进去直接排序。

2,正向双指针

  • 因为已经排好序,所以比较最小只需要比较最靠前的元素即可。
  • 这里就能想到双指针,但是这个方法必须新建一个新数组,空间复杂度高。
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int[] result = new int[m+n];
        int i=0,j=0,r=0;
        while(i<m||j<n){
            if(i==m){
                result[r] = nums2[j];
                j++;
            }
            else if(j==n){
                result[r] = nums1[i];
                i++;
            }
            else if(nums1[i]<nums2[j]){
                result[r] = nums1[i];
                i++;
            }
            else{
                result[r] = nums2[j];
                j++;
            }
            r++;
        }
        for(int q = 0;q<m+n;q++){
            nums1[q] = result[q];
        }
    }
}

3,反向双指针

  • 正向双指针的逆想法
  • 如果不想新建新数组的话,用最小覆盖可能会覆盖nums1原来的元素
  • 所以我们可以想到用最大来覆盖,nums1最后的元素,因为nums1长度固定。
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m - 1;
        int j = n - 1;
        int r = m+n -1;
        while(i>=0||j>=0){
            if(i==-1){
                nums1[r] = nums2[j];
                j--;
            }
            else if(j==-1){
                nums1[r] = nums1[i];
                i--;
            }
            else if(nums1[i]>nums2[j]){
                nums1[r] = nums1[i];
                i--;
            }
            else{
                nums1[r] = nums2[j];
                j--;
            }
            r--;
        }
    }
}

理解

1,审题。

5,二叉树遍历

在算法(八)那篇文章里。

很重要。很重要。很重要。很重要。很重要。很重要。很重要。很重要。很重要。很重要。

正文完