【算法】代码随想录训练营Day3打卡,链表,leetcode203.移除链表元素 707.设计链表206.反转链表

链表

代码随想录算法训练营Day13任务。

  1. 认识链表
  2. leetcode203. 移除链表元素
  3. leetcode707. 设计链表
  4. leetcode206. 反转链表

认识链表

链表是一种比较常见的数据结构,同样也是线性表。链表其实是利用了内存中零散的内存空间,其中通过指针指向某个内存空间的地址来达到线性的一个存储结构。

链表一般分为
单链表
双链表
循环链表

单链表

单链表是数据结构中最常用的链表,也是算法题中经常考察的,他的每一个节点中都拥有两个属性值

  1. value 表示当前节点的值
  2. next指针,表示链表下一节点的地址
    单链表只有当前节点下一节点的值,所以单链表只能从头寻到尾,并不能反向寻找

双链表

双链表相比单链表多了一个prev的属性,该属性指向的是上一个节点的地址,所以双链表是可以从头寻到尾,也可以反之寻找

循环链表

循环链表其实可以说是单链表的一种特殊情况,他在某个节点的next属性中存储的是链表内另外一个节点的地址,这样就形成了一个闭环,如果一直根据链表寻下去,永远都不会找到null

在C语言和JS中都没有链表这种数据结构,所以需要我们自己来定义链表

C语言手写链表

struct ListNode {
	int val;
	struct ListNode *next //next是一个指针
}

js手写链表

// 定义一个构造函数,到时候就可以使用 new 构造出链表节点
function ListNode(val,next){
	  this.val = (val===undefined ? 0 : val)
      this.next = (next===undefined ? null : next)
}

移除链表元素

力扣链接:https://leetcode.cn/problems/remove-linked-list-elements/description/
先写一个例子
链表 1-3-6-5-null
target = 6
这道题其实我思考出有两种解法
1.是使用双指针方法,这种方法也就是原地修改的方法,不需要使用虚拟节点,但是会比较麻烦,慢指针slot初始化为head,快指针fast初始化为head.next,当fast.val != null时 我们开始判断如果,fast.val != target 那么我们移动 fast和slot指针,否则,我们就移动fast指针,并且判断移动后的fast.val 是否为null 如果为null表示我们走到链表尽头了,直接slot.next = null 就ok,若不为null, 但 fast.val != target slot.next = fast 就ok,若都不满足以上条件,则时 fast.val == target 那我们就不用动,进入下一次循环的时候会重复以上的判断。
当fast指针到null时循环结束,链表内的节点我们就基本都删除完毕了,但还有头部节点我们还没有判断到,因为我们fast时从head.next开始的,这时候我们单独判断head.val = targe 如果时 则把head.next赋值为头节点
在这里插入图片描述
2.使用虚拟链表法
我们创造一个虚拟的头节点,并将该节点的next指向head,
我们创造一个指针cur,指向虚拟头节点,每次判断cur.next.val == target
若 = targe 则 cur = cur.next.next
若 != target 则 cur = cur.next
在这里插入图片描述

C语言版本

虚拟头节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val){
    typedef struct ListNode ListNode;
    ListNode *shead; //创建虚拟头节点并开辟一片内存空间
    shead = (ListNode *)malloc(sizeof(ListNode));
    shead->next = head;// c语言 指针使用 -> 访问属性值
    ListNode *cur = shead;
    while(cur->next  != NULL){
        if(cur->next->val == val){
            ListNode *tmp = cur->next; // 这里不需要开辟新的内存空间,我们只需要保存指向的内存空间地址就行
            cur->next = cur->next->next;
            free(tmp); // 删掉的节点要主动释放内存空间
        }else{
            cur = cur->next;
        }
    }
    head = shead->next;
    free(shead);
    return head;
}```


### JS版本

方法1

```javascript
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    if(head == null  ){
        return head
    }

    let slot = head;
    let fast = head.next;
    while(fast != null){
        if(fast.val != val){
            slot = slot.next;
            fast = fast.next;
        }else{
            fast = fast.next;
            if(fast == null){
                slot.next = null
            }else if(fast.val != val  ){
                slot.next = fast
            }
        }
    }
    if(head.val == val){ head = head.next } // 一定要注意判断头节点 否则会有错
    return head;
};

方法2

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    let newHead = new ListNode(0,head);
    let cur = newHead
    while(cur.next){
        if(cur.next.val != val){
            cur = cur.next
        }
        else{
            cur.next = cur.next.next;
        }
    }
    return newHead.next

}

设计链表

https://leetcode.cn/problems/design-linked-list/

这道题我们先读题干,这道题比较长,部分函数只会用js来书写
我们一共要实现
get(index); 获得index节点的val的值
addAtHead(val); 头节点增加一个节点
addAtTail(val); 在尾节点增加一个节点
addAtIndex(index,val); 在index节点前增加一个节点
deleteAtIndex(index); 删除index节点

首先我们得实现一个LinkNode的方法实现链表节点

function LinkNode(val,next) {
        this.val = val;
        this.next = next;
}

接着再实现一个链表的方法,我们将再这个方法的原型上书写各种方法

var MyLinkedList = function() {
	// 在链表中 我们要有 大小,头节点,尾节点  三个属性以便我们使用
    this._size = 0;
    this._head = null;
    this._tail = null;
};

get(index)

在这里插入图片描述
因为后面的getAtIndex与deleteAtIndex都需要用到寻找某一个节点的功能,所以我们把getNode单独封装成一个方法
代码

MyLinkedList.prototype.getNode = function(index){
   // if(index < 0 || index >= this._size) return null;
    let cur = new LinkNode(0, this._head);
    while(index-- >= 0) {
        cur = cur.next;
    }
    return cur;
}

/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function(index) {
    if(index < 0 || index >= this._size) return -1;
    // 获取当前节点
    return this.getNode(index).val;
};

addAtHead(val)

这个方法比较简单,我们直接在 _head的前面new一个节点,将next指向_head
这里需要判断一下 若是只有一个节点那么这个节点即是头节点 也是尾节点
在这里插入图片描述

MyLinkedList.prototype.addAtHead = function(val) {
    
    let node = new LinkNode(val,this._head);
    this._head = node;
    this._size++;
    if(this._size == 1){
    	this._tail = node
	}
    
};

addAtTail(val)

这个方法是在尾部插入一个节点,那么我们直接通过tail方法可以拿到尾部节点,若是tail == null,我们直接吧tail设置为新节点就好了,若是不为null,则需要先把tail.next指向新节点,再把tail设置为新节点
在这里插入图片描述

MyLinkedList.prototype.addAtTail = function(val) {
    let node = new LinkNode(val,null);
    if(!this._tail){
        this._size++;
        this._tail = node;
    }else{
        this._tail.next = node;
        this._tail = node;
        this._size++;
    }
    if(this._size == 1){
        this._head = this._tail
    }
    
    

};

addAtIndex(index,val)

这道题根据index来判断,若index>size 则 直接ruturn 不进行操作
若index <= 则执行 addAtHead方法
若index == size 则执行 addAtTail方法
若不满足以上规则,则是在边界内的
我们通过 getNode方法取到index的前一个节点,
在通过指针改变实现新增
在这里插入图片描述

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if(index > this._size){
        return 
    } 
    if(index<=0){
        this.addAtHead(val);
        return;
    } 
    if(index == this._size){
        this.addAtTail(val);
        return
    }
        const node = this.getNode(index - 1);
        node.next = new LinkNode(val, node.next);
        this._size++;

    

};

deleteAtIndex(index)

在这里我们要判断index的边界 若index<0或index>=size 则index是无效的,则不进行操作
若 index == 0 则表示要删除头节点,我们在删除后要把 head修改为head.next
接下来我们通过getNode 找到index-1的节点,然后将next指向 他的next.next节点就完成了删除
然后我们要判断若是删除后 index= size-1 则表示是删除的是最后一个节点,则要将tail设置为index-1的节点

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if(index<0 || index>this._size-1){
        return 
    }
    if(index == 0){
        this._head = this._head.next;
        if(index === this._size - 1){ //判断是否是最后一个节点
            this._tail = this._head
        }
        this._size--;
        return;
    }
    let node = this.getNode(index-1);
    node.next = node.next.next;
    if(index === this._size - 1) {
        this._tail = node;
    }
    this._size--;
};

LeetCode 206. 反转链表

https://leetcode.cn/problems/reverse-linked-list/
反转链表我们首先准备一个虚拟头节点newHead next指向null
准备两个指针一个cur = head pre = cur.next;
每一次我们把 newHead.next 指向 cur,
cur.next指向 newHead.next
在把cur的指针zhixiang pre,
下一次时pre又置为 cur.next
如图
在这里插入图片描述
这样只要我们返回 newHead.next 就是一个反转过的链表了

C语言解法

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
   struct ListNode newHead = {0,NULL}; //这里要注意newHead虚拟头节点时一个变量,而不是指针变量
   struct ListNode* pre;
   struct ListNode* cur =  head;
   while(cur){
      pre = cur->next;
      cur->next = newHead.next;
      newHead.next = cur;
       cur = pre;
   }
   return newHead.next;


}

JS解法

/**
 * 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 newHead = new ListNode(0,null);
    let cur = head;
    while(cur){
        let pre = cur.next;
        cur.next =  newHead.next; //一定要注意cur.next 与 newHead.next的赋值顺序
        newHead.next = cur;
        cur = pre;

        
    }
    return newHead.next;
};

今日心得

今日完成算法+完成文章的总共时间共4小时,以前刷链表主要在反转链表和设计链表这两道题过的很快,并没有特别深刻的去理解这两道题,今日将每一道题都细细分解,仔细画图去理顺思路,让今日对链表的题目又很大的收获,并且在c语言刷反转链表的时候其实遇到了变量和指针变量的坑,也让我对c语言指针要熟悉一些

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值