当我学习算法时(链表)

43 阅读5分钟

链表

根据数组来拓展思路:

1.如果我们对一个数组进行去重的同时再对数组的长度进行测长,我们使用双指针的方法,即一个fast指针,一个slow指针:

思路:

(1)fast指针相当于是数组中领路的一个角色

(2)slow指针则是比fast指针慢

(3)先保证数组非空,其次判定fast指针的长度不超过数组的长度

(4)fast先行,如果fast遍历到一个和slow指针不同的数据就让slow为当前这个值

(5)由此可以保证[0...slow]这个区间的值不是重复的。

代码实现:

export default function change_length(){
const arr = [1,1,2,3,4,4,5,6];
let fast = 0;
let slow = 0;
while(fast<arr.length{
    if(arr[fast]!==arr[slow]){
        arr[slow] = arr[fast];
        slow++;
    }
    fast++;
}

return arr.length();
}

单链表的去重

逻辑是一样的,不一样的就是数组的赋值操作 变成了指针而已

function deleteListnode(ListNode head){
    if(head==null) return null;
    ListNode slow  = head,fast = head;
    while(fast.next!==null){
        if(fast.val!==slow.val){
            slow.next = fast;
            slow = slow.next;
        }
        fast = fast.next;
        
    }
    
    slow.next = null;
    return head;
}

元素移除

给定一个数组和一个数值,移除这个数组中的所有该数值并返回数组的内容和长度

function deleteNums(arr,num){
    let fast,slow=0;
    while(arr[fast]<arr.length){
        if(arr[fast] !== num ){
            arr[slow] = arr[fast];
            slow++;
        }
        fast++;
    }
    return slow;
}

两数之和-输入有序的数组

给予一个整数的数组,该数组已经排过序了,求两个数相加为target

function addTarget(arr,target){
    let left = 0;
    let right = arr.length-1;
    while(left<right){
        let sum = arr[left]+arr[right];
        if(sum === target){
            return left+1,right+1
        }
        if(sum<target){
            left++;
        }else if(sum>target){
            right--;
        }
    }
}

反转数组

function reverse(arr){
    let left=0;
    let right = arr.length;
    
    while(left<right){
        let temp = arr[right]
        arr[right] = arr[left]
  		arr[left] = temp
        left++;
        right--;
    }
}

链表反转

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head 
 * @return {ListNode}
 */
var reverseList = function(head) {
    //设置前节点
   let left =null;
   //设置当前节点
   let right = head;

   while(right!==null){
       //保存right的下一个值到零时节点
    let temp = right.next;//3
       //让当前节点的下个值转化为比当前小的值
    right.next = left;//1
       //让当前的值为
    left = right;//2
    right = temp;//3

   }
   return left;
    }

    

回文串的判断

只需要判断左右的字符是否相等即可

function isHuiwen(arr){
    let left = 0;
    let right =arr.length-1;
    while(left<right){
        if(arr[left]!==arr[right]){
            return false;
        }
        left++;
        right--;
    }
    return true;
}

最长回文串的判断

![image-20250120215130994](C:\Users\Harara lazer!\AppData\Roaming\Typora\typora-user-images\image-20250120215130994.png)

这个的逻辑仍然是使用双指针的方法,如果使用双指针进行设计的话,这次就不再是从两边走而是从中间走;

aba 中间轴就是b abba 中间轴有两个

所以:我们设计两种不同的情况

function theLongestArr(arr, l, r) {
    while (l >= 0 && r < arr.length && arr[l] === arr[r]) {
        l--;
        r++;
    }
    return arr.slice(l + 1, r); // 使用 slice 来提取子串
}

function longestPalindrome(arr){
    let arr0 = '';
    for(let i =0;i<arr.length;i++){
        let arr1 = theLongestArr(arr,i,i);
        let arr2 = theLongestArr(arr,i,i+1);
        
        arr0 = arr0.length>arr1.length?arr0:arr1;
        arr0 = arr0.length>arr2.length?arr0:arr2;
    }
    return arr0;
}

双指针的单链表的问题

还是一样和数组的操作类似:

(1)创建一个新的数组

(2)比较两个数组的大小,哪个小取哪个

(3)把较小的那个放入新建的数组

function ListNode(l1,l2){
    //创建一个头节点
    ListNode dummy = new ListNode(-1),p=dummy;
    //创建两个链表用来代表给定的链表
    ListNode p1 = l1,p2 = l2
    while(p1!==null&&p2!==null){
        if(p1.val<p2.val){
            p.next = p1.val;
            p1=p1.next;
        }else{
            p.next = p2;
            p2 = p2.next;
        }
        p = p.next
    }
    //循环结束之后,如果其中一个链表已经遍历完了代表它之后的元素都进入了新的链表,所以我们只需要将另外一个链表的剩下的所有值接入即可
    if(p1!==null){
        p.next = p1;
        
    }
    if(p2!==null){
        p.next = p2;
    }
    return dummy.next
}

单链表的分解

![image-20250121145113520](C:\Users\Harara lazer!\AppData\Roaming\Typora\typora-user-images\image-20250121145113520.png)

class ListNode {
    constructor(val, next = null) {
        this.val = val;
        this.next = next;
    }
}

function add(head, x) {
    let dummy1 = new ListNode(-1);
    let dummy2 = new ListNode(-1);
    let p1 = dummy1;
    let p2 = dummy2;
    let p = head;
    while (p!== null) {
        if (p.val >= x) {
            p2.next = p;
            p2 = p2.next;
        } else {
            p1.next = p;
            p1 = p1.next;
        }
        let temp = p.next;
        p.next = null;
        p = temp;
    }
    p1.next = dummy2.next;
    return dummy1.next;
}

求链表的倒数第k个节点(双指针的思路)

(1)一个链表的长度由k和n-k的长度来组成

(2)比如我们取倒数第九个节点而长度一共为10,那么倒数第九个就是10-9=1,也就是第1个节点

(3)所以我们使用一个指针先去遍历这个链表的长度,然后当它先走k之后,再引入第二个指针,此时第二个指针和第一个指针齐头并进走n-k

(4)此时第一个走完了整个链表,第二个刚好走到倒数第k个节点

class ListNode{
    construct(val,next=null){
        this.val = val;
        this.next = next;
    }
}
function thekofnode(head,k){
    ListNode p1 = head;
    for(let i =0;i<k;i++){
        p1=p1.next;
    }
    ListNode p2 = head;
    while(p1!==null){
        p2=p2.next;
        p1=p1.next;
    }
    return p2;

}

删除倒数第K个节点

function deleteNode(head,k){
    //虚拟头节点,其目的是为了如果我们删除head节点,那么没有倒数第二个节点,所以使用一个虚拟头节点来删除head
    ListNode dummy = new ListNode(-1);
    
    dummy.next = head;
    ListNode x = thekofnode(dummy,k+1);
    x = x.next.next;
    return dummy.next
    
}

求单链表的中点

当fast节点走两步,slow节点走一步,这就意味着fast节点走完整个链表,slow节点刚好在链表的中间

class ListNode{
    construct(val,next=null){
        this.val=val;
        this.next = next;
    }
}
ListNode head;
function themidNode(head){
    ListNode slow = head,fast = head;
    while(fast!=null&&fast.next!==null){
        slow = slow.next;
        fast = fast.next.next;
        
    }
    return slow;
}


判断 链表是否是一个环

也是一样的思路 fast走两步slow走一步,如果fast遍历完之后为空那就不是环,如果fast和slow相遇就是环

class ListNode{
    construct(val,next=null){
        this.val=val;
        this.next = next;
    }
}
ListNode head;
function themidNode(head){
    ListNode slow = head,fast = head;
    while(fast!=null&&fast.next!==null){
        slow = slow.next;
        fast = fast.next.next;
        if(fast===slow)return true;
    
    }
    
    return false;
}


判断链表是否相交

![image-20250121160028704](C:\Users\Harara lazer!\AppData\Roaming\Typora\typora-user-images\image-20250121160028704.png)

遍历完链表1之后继续遍历2,2链表也是一样的思路,让两个链表配平。

function searchSameNode(l1,l2){
    ListNode p1 = l1,p2 =l2;
    while(p1!==p2){
        if(p1!==null){
            p1=p1.next;
        }else{
            p1 = l2;
        }
         if(p2!==null){
            p2=p2.next;
        }else{
            p2 = l1;
        }
    }
    return p1;
}