探索数据结构小紫书之线性表

·  阅读 105

持续更新中......

线性表分为顺序表示和链式表示,这篇文章重点描述链式表示,也就是链表结构。

基本概念

链表分为:线性链表,循环链表,双向链表

  • 线性表的链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)

  • 一个结点,包含两个域,数据域和指针域

  • n个结点链结成一个链表,即为线性表

各种操作

反转链表

解法一:递归

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    //以下一个结点作为头结点
    if (head == null || head.next == null) {
        return head;
    }
    //递归
    const newHead = reverseList(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
};
复制代码

解法二:迭代+双指针

//迭代! 双指针
var reverseList = function(head) {
    let prev = null;//前指针
    let curr = head;//当前指针 
    while (curr) {
        const next = curr.next;//当前指针的下一个 存起来
        curr.next = prev;
        prev = curr;//前进
        curr = next;//前进
    }
    return prev;
};
复制代码

环形链表

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。

//给每个节点打标记
const hasCycle = function(head) {
  while (head) {
    if (head.tag) {
      return true;
    }
    head.tag = true;
    head = head.next;
  }
  return false;
};
复制代码

链表中倒数第k个节点

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.
复制代码

解法:快慢指针

var getKthFromEnd = function(head, k) {
    //p慢指针,q快指针,q先走k步,然后p q一起走 当q节点为空,则返回p
    let p=head,q=head,i=0
    while(q){
        if(i>=k){
            p=p.next
        }
        q=q.next
        i++
    }
    return i>=k ? p : null
};
复制代码

合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

输入: l1 = [1,2,4], l2 = [1,3,4]
输出: [1,1,2,3,4,4]
复制代码
输入: l1 = [], l2 = [0]
输出: [0]
复制代码

解法:设置哑结点+迭代

var mergeTwoLists = function(l1, l2) {
    const prehead=new ListNode(-1);//设置一个哑节点,值为-1 可以用来返回整个链表

    let prev = prehead;//设置一个动态节点  用来移动
    while(l1!=null && l2!=null){//当有一个或两个都到头了跳出while
        if(l1.val<=l2.val){
            prev.next=l1;//当l1更小,那么指向l1
            l1=l1.next//l1变为 l1.next  迭代
        }else{
            prev.next=l2//同理
            l2=l2.next//同理
        }
        prev=prev.next//prev像后移一位
    }
    //合并到l1或者l2到头,判断谁还有,就把谁接上
    prev.next = l1 === null?l2:l1
    return prehead.next//返回链表
};

复制代码

相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

image.png

题目数据 保证 整个链式结构中不存在环

注意,函数返回结果后,链表必须 保持其原始结构

解法一:哈希表+遍历

//相交链表,用哈希表解决
var getIntersectionNode = function(headA, headB) {
    const visited = new Set();//Set结构  只存key 且key不重复
    let temp = headA;
    while(temp!==null){
        visited.add(temp)//把headA存入visited
        temp=temp.next
    }
    temp = headB;
    while(temp!==null){
        if(visited.has(temp)){
            return temp
        }
        temp=temp.next//遍历headB 
    }
    return null;//没找到就返回null
};
复制代码

解法二:交替+前后

var getIntersectionNode = function(headA, headB) {
        if(headA===null || headB === null){
            return null
        }//有任何一个是空 则没有交点
        let pA =headA,pB=headB
        while(pA!==pB){//当没找到相交点的时候
            pA= pA===null ? headB : pA.next//没有找到就换个链表找
            pB= pB===null ? headA : pB.next//同上
        }//总是有人会先交换,有人后交换
        //(除非两个链表长度一样,如果这样第一次就可以找到,不用交换)
        //因为有这个先后,所以迟早会遇到走一样路程就可以到相交点的情况
        return pA
};
复制代码

回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。

  示例 1: image.png

输入:head = [1,2,2,1] 输出:true

解法:转为数组+双指针

 //回文是指前后读起来都一个样子
 //先存为数组 然后用双指针
var isPalindrome = function(head) {
    let current=head
    const arr=[]//创建一个数组保存 链表的值
    while(current){
        arr.push(current.val)
        current=current.next
    }
    let p,q;//双指针    
    for(let i=0;i<arr.length;i++){
        p=arr[i]
        q=arr[arr.length-1-i]
        if(p!=q){//当遇到他们不一样时 则不对称
            return false
        }
    }
    return true
};
复制代码
分类:
代码人生