链表

常用的链表结构

  • 单链表
    单链表
  • 双向链表 空间换时间
    双向链表
  • 循环链表
    循环链表
    链表的删除/插入是O(1)级别的,但是随机访问需要O(n)的复杂度
  • 双向循环链表
    双向循环链表

技巧

  • 理解指针或引用的含义

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

在编写链表代码的时候,我们经常会有这样的代码:p->next=q。这行代码是说,p 结点中的 next 指针存储了 q 结点的内存地址。

还有一个更复杂的,也是我们写链表代码经常会用到的:p->next=p->next->next。这行代码表示,p 结点的 next 指针存储了 p 结点的下下一个结点的内存地址。

  • 警惕指针丢失和内存泄漏

注意不要把指针弄丢,比如插入是时候

插入

在删除的时候要注意,释放内存

  • 利用哨兵简化实现难度

哨兵
在处理删除时,head节点比较难处理,所以添加一个哨兵,然后处理完,返回哨兵的next

  • 重点处理边界条件

经常用来检查链表代码是否正确的边界条件有这样几个:

  1. 如果链表为空时,代码是否能正常工作?
  2. 如果链表只包含一个结点时,代码是否能正常工作?
  3. 如果链表只包含两个结点时,代码是否能正常工作?
  4. 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?
  • 举例画图,辅助思考,多建指针

写在纸上,把指针赋值和调整之后的结果也画出来,对照写,就比较简单了
插入

练习题

共25个已完成13个,全是中等

个常见的链表操作:

  • 1.单链表反转 206
    单链表反转
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */

var reverseList = function(head) {
    var prev = null;
    var cur = head;
    var next = null;
    while (cur !== null) {
        next = cur.next;
        cur.next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

// 链表的递归实现
var reverseList = function(H) {
	if (H === null || H.next === null)
		//链表为空直接返回,而H->next为空是递归基
		return H
	var newHead = reverseList(H.next) //一直循环到链尾
	H.next.next = H //翻转链表的指向
	H.next = null //记得赋值NULL,防止链表错乱
	return newHead //新链表头永远指向的是原链表的链尾
}
  • 2.链表中环的检测 141
    快慢指针
使用set来存储

var hasCycle = function(head) {
    const set = new Set();
    if (head == null) {
		return false;
	}
    while(head.next !== null) {
        if(set.has(head)) {
            return true;
        } else {
            set.add(head);
        }
        head = head.next;
    }
    return false;
};

使用快慢指针

var hasCycle = function(head) {
    if (head == null || head.next == null) {
        return false;
    }
    let slow = head;
    let fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
};
  • 3.两个有序链表合并 21
var mergeTwoLists = function(l1, l2) {
    let preHead = new ListNode();
    let cur = preHead;

    while (l1 && l2){
        if(l1.val <= l2.val){
            cur.next = new ListNode(l1.val);
            l1 = l1.next;
        }
        else {
            cur.next = new ListNode(l2.val);
            l2 = l2.next;
        }
        cur = cur.next;
    }
    cur.next = l1 || l2
    return preHead.next;
};
  • 4.删除链表倒数第n个节点 19
var swapPairs = function(head) {
    var dummyHead = new ListNode(0);
    dummyHead.next = head;
    p = dummyHead;
    while(p.next && p.next.next) {
        var node1 = p.next;
        var node2 = p.next.next;
        var next = node2.next;
        
        node2.next = node1;
        node1.next = next;
        p.next = node2;
        
        p = node1
    }
    if (dummyHead.next === null) {
        return []
    }else {
        return dummyHead.next;
    }
    
};
  • 5.求链表的中间节点 876
    快慢指针
空间换时间  O(n) 空间复杂度:O(N)
var middleNode = function(head) {
    const arr = [];
    while(head !== null) {
        arr.push(head);
        head = head.next;
    }
    const middle = Math.floor(arr.length / 2);
    return arr[middle];   
};

快慢指针

var middleNode = function(head) {
    slow = fast = head;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;   
};


  • 6.两两交换链表中的节点 24 需要补充图
var swapPairs = function(head) {
    var dummyHead = new ListNode(0);
    dummyHead.next = head;
    p = dummyHead;
    while(p.next && p.next.next) {
        var node1 = p.next;
        var node2 = p.next.next;
        var next = node2.next;
        
        node2.next = node1;
        node1.next = next;
        p.next = node2;
        
        p = node1
    }
    if (dummyHead.next === null) {
        return []
    }else {
        return dummyHead.next;
    }
    
};
  • 7.删除链表中的节点 237
把下一个的值赋给这个node,然后删除下一个节点即可

var deleteNode = function(node) {
  node.val = node.next.val;
  node.next = node.next.next;
};
  • 8.删除排序链表中的重复元素 83
因为是有序的,所以直接前一个跟后一个对比删除就行了时间复杂度O(N)
如何是无序的使用set也可以做到O(N), 就是空间换时间

var deleteDuplicates = function(head) {
    if (head == null) {
        return [];
    }
    let cur = head;
    while(cur && cur.next){
        if (cur.val === cur.next.val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next
        }
    }
    return head;
};
  • 9.移除链表元素 203
var removeElements = function(head, val) {
    while (head !== null && head.val === val) {
        head = head.next;
    }
    
    if (head === null) {
        return []
    }
    let cur = head;
    while(cur.next !== null) {
        if (cur.next.val === val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next;
        }
    }
    return head;
};

<!--递归-->
if (head == null){ 
    return null;
    } 
head.next = removeElements(head.next,val); 
return head.val == val ? head.next:head;
    
// 使用头部守位   然后返回 dummnyHead.next
    
var removeElements = function(head, val) {
    let dummnyHead = new ListNode(0);
    dummnyHead.next = head;
    let cur = dummnyHead;
    while(cur.next !== null) {
        if (cur.next.val === val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next;
        }
    }
    if (dummnyHead.next === null){
        return []
    } else {
       return dummnyHead.next
    }
};
  • 10.回文链表 234
快慢针找到中间,然后把后面的翻转,然后对比
var reverseList = function(head) {
    var pre = null;
    var cur = head;
    var next = null;
    while(cur !== null) {
        next = cur.next;
        
        cur.next = pre;
        
        pre = cur;
        cur = next;
    }
    return pre;
}

var isPalindrome = function(head) {
    if(head === null || head.next === null) {
        return true;
    }
    fast = slow = head;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }
    if(fast) slow = slow.next;
    let list2 = reverseList(slow);
    while(list2 !== null) {
        if (head.val !== list2.val) {
            return false;
        }
        head = head.next;
        list2 = list2.next;
    }
    return true;
};
  • 11.旋转链表 61
  • 12.相交链表 160
先变量两个表,然后把长度磨平,然后在对比节点是否相等
var getIntersectionNode = function(headA, headB) {
    if(headA === null || headB === null) {
        return null;
    }
    var listA = headA;
    var listB = headB;
    
    var lenA = 0;
    var lenB = 0;
    while(listA !== null) {
        listA = listA.next;
        lenA++;
    }
    while(listB !== null) {
        listB = listB.next;
        lenB++;
    }
    if (lenA > lenB) {
        var diff = lenA - lenB;
        while(diff>0) {
            headA = headA.next;
            diff--;
        }
    }
    if (lenB > lenA) {
        var diff = lenB - lenA;
        while(diff>0) {
            headB = headB.next;
            diff--;
        }
    }
    
    while(headB!=null) {
        if(headA==headB)
            return headA;
        headA=headA.next;
        headB=headB.next;
    }
    return null;
};
public class Solution {
    
    ListNode head;
    Random random;
    
    public Solution(ListNode h) {
        head = h;       
        random = new Random();        
    }
    
    public int getRandom() {
        
        ListNode c = head;
        int r = c.val;
        for(int i=1;c.next != null;i++){
            
            c = c.next;
            if(random.nextInt(i + 1) == i) r = c.val;                        
        }
        
        return r;
    }
}
var len = 0, curr = root;
    while (curr) {
        len++;
        curr = curr.next;
    }
    var quo = Math.floor(len / k);
    var rem = len % k;
    var results = [root];
    curr = root;
    var last = curr;

    while (k > 1) {
        for (var i = 0; i < quo; i++) {
            last = curr;
            curr = curr.next;
        }
        if (rem > 0) {
            last = curr;
            curr = curr.next;
            rem--;
        }
        if (last) {
            last.next = null;    
        }
        results.push(curr);
        k--;
    }
    return results.map(item => item === null ? []: item);

先遍历一遍,拿到长度,然后len / k 就是要分成几个,然后len % k 就是有一个是多1的

[1,2,3,4,5,6,7,8,9,10]
===>
[4,3,3]

  • 20.删除排序链表中的重复元素 83
var deleteDuplicates = function(head) {
    if (head == null) {
        return [];
    }
    let cur = head;
    while(cur && cur.next){
        if (cur.val === cur.next.val) {
            cur.next = cur.next.next
        } else {
            cur = cur.next
        }
    }
    return head;
};
  • 21.奇偶链表 328
  • 22.两数相加 2
var addTwoNumbers = function(l1, l2) {
    if (!l1) return l2
    if (!l2) return l1
    let p1 = l1
    let p2 = l2
    let val, carry
    const ret = new ListNode()
    let cur = ret
    while (p1 || p2) {
        let current = add(p1, p2, carry)
        cur.val = current.val
        carry = current.carry
        if (p1) p1 = p1.next
        if (p2) p2 = p2.next
        if (p1 || p2) {
            cur.next = new ListNode()
            cur = cur.next
        }
    }
    if (carry) {
        cur.next = new ListNode(carry)
    }
    return ret
};

function add(p1, p2, _carry = 0) {
    let val = ((p1 && p1.val) || 0) + ((p2 && p2.val) || 0) + _carry
    let carry = (val >= 10) ? 1 : 0
    if (carry) { val -= 10 }
    return { val, carry }
}
  • 23.删除排序链表中的重复元素 II82
  • 24.k个一组翻转链表 25
posted @ 2018-11-29 11:30  快乐~  阅读(197)  评论(0编辑  收藏  举报