【前端er每日算法题】Leetcode 链表3道题-203移除链表元素707设计链表206反转链表

116 阅读4分钟

一天当中真正的程序员工作从晚上开启,幸好娃睡了,我可以刷刷题,拖了2天没有跟上,今天必须跟上。今天题目是三道链表相关的。

203.移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。  

示例 1: image.png

思路

这个题比较简单,直接遍历比较,需要注意的是对于链表的删除操作,头节点的删除跟其他不一样,其他节点都是通过找到被删除节点的上一个节点来删除,所以删除操作如果要保持一致,则构造一个哑节点会方便一些,这样删除操作就统一了。

/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var removeElements = function(head, val) {
    let dummy = new ListNode()
    dummy.next = head
    let pre = dummy
    let cur = head
    while(cur){
        if (cur.val == val){
            cur = cur.next
            pre.next = cur
        } else {
            pre = cur
            cur = cur.next
        }
    }
    return dummy.next
};

// 8.28 也可以只定义一个中间节点
var removeElements = function(head, val) {
    const dummy = new ListNode();
    dummy.next = head;
    let cur = dummy;
    while(cur.next) {
        if (cur.next.val === val) {
            cur.next = cur.next.next;
        } else {
            cur = cur.next;
        }
    }
    return dummy.next;
}

707.设计链表

思路

这个题就比较有意思了,我读题读了好多遍,不明白它写的是个啥意思,这题看的不难,但是一开始没想起来构造函数应该写什么,就直接看了题解,原来需要在构造函数里定义一个head和size,好了,这个有了,差不多就知道怎么写了,其余部分类似写业务代码,跟着需求来。

但是通过后再看解题,妈呀,好像跟我写的不一样,我这个没有用虚拟头节点,插入删除都对头节点单独处理了,并且是有真实的值的,而题解是用的头节点,所以跟我的写法不一样,插入删除操作都是统一的,所以附上两种写法。

其次还有双向列表写法,

无虚拟头节点写法
function ListNode(val, next) {
    this.val = (val===undefined ? 0 : val)
    this.next = (next===undefined ? null : next)
}

var MyLinkedList = function() {
    this.head = new ListNode();
    this.size = 0;
};

/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function(index) {
    if (index >= this.size || index < 0) {
        return -1
    }
    
    let node = this.head;
    while (index-- > 0) {
        node = node.next;
    }
    return node.val
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    this.addAtIndex(0, val);
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    this.addAtIndex(this.size, val)
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if (index > this.size || index < 0) {
        return;
    }
    let node = this.head;
    const newNode = new ListNode(val);
    this.size++;
    // 这里相当于对头节点单独处理了,head并不是虚拟头节点
    if (index === 0) {
        newNode.next = this.head;
        this.head = newNode
        return;
    }
    while (--index > 0) {
        node = node.next
    }
    let temp = node.next;
    node.next = newNode;
    newNode.next = temp;
};

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if (index < 0 || index >= this.size) {
        return;
    }
    this.size--;
    // 对头节点处理
    if (index === 0) {
        this.head = this.head.next;
        return;
    }
    let node = this.head;
    while(--index > 0) {
        node = node.next;
    }
    node.next = node.next.next
};

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * var obj = new MyLinkedList()
 * var param_1 = obj.get(index)
 * obj.addAtHead(val)
 * obj.addAtTail(val)
 * obj.addAtIndex(index,val)
 * obj.deleteAtIndex(index)
 */
虚拟头节点写法
function ListNode(val, next) {
    this.val = (val===undefined ? 0 : val)
    this.next = (next===undefined ? null : next)
}

var MyLinkedList = function() {
    this.head = new ListNode(); // 作为虚拟头节点,不存储值,方便操作
    this.size = 0;
};

/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function(index) {
    if (index >= this.size || index < 0) {
        return -1
    }
    
    let node = this.head;
    while (index-- >= 0) {
        node = node.next;
    }
    return node.val
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    this.addAtIndex(0, val);
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    this.addAtIndex(this.size, val)
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if (index > this.size || index < 0) {
        return;
    }
    let node = this.head;
    const newNode = new ListNode(val);
    this.size++;
    for (let i = 0; i < index; i++) {
        node = node.next;
    }
    let temp = node.next;
    newNode.next = temp;
    node.next = newNode;
};

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if (index < 0 || index >= this.size) {
        return;
    }
    this.size--;
    let node = this.head;
    for (let i = 0; i < index; i++) {
        node = node.next;
    }
    node.next = node.next.next
};
双向列表写法
var MyLinkedList = function() {
    this.head = new ListNode();
    this.tail = new ListNode();
    this.head.next = this.tail;
    this.tail.prev = this.head;
    this.size = 0;
};

/** 
 * @param {number} index
 * @return {number}
 */
MyLinkedList.prototype.get = function(index) {
    if (index >= this.size || index < 0) {
        return -1
    }
    
    let node;
    if (index < this.size - index) {
        node = this.head;
        while (index-- >= 0) {
            node = node.next;
        }
    } else {
        node = this.tail;
        for (let i = 0; i < this.size - index; i++) {
            node = node.prev;
        }
    }
    return node.val
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtHead = function(val) {
    this.addAtIndex(0, val);
};

/** 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtTail = function(val) {
    this.addAtIndex(this.size, val)
};

/** 
 * @param {number} index 
 * @param {number} val
 * @return {void}
 */
MyLinkedList.prototype.addAtIndex = function(index, val) {
    if (index > this.size || index < 0) {
        return;
    }
    let node;
    const newNode = new ListNode(val);
    // 找到的都是前驱节点
    if (index < this.size - index) {
        node = this.head;
        for (let i = 0; i < index; i++) {
            node = node.next;
        }
    } else {
        node = this.tail;
        for (let i = 0; i < this.size - index + 1; i++) {
            node = node.prev;
        }
    }
    this.size++;
    newNode.prev = node;
    newNode.next = node.next;
    let temp = node.next;
    node.next = newNode;
    temp.prev = newNode;
};

/** 
 * @param {number} index
 * @return {void}
 */
MyLinkedList.prototype.deleteAtIndex = function(index) {
    if (index < 0 || index >= this.size) {
        return;
    }
    // 找到的都是前驱节点
    if (index < this.size - index) {
        node = this.head;
        for (let i = 0; i < index; i++) {
            node = node.next;
        }
    } else {
        node = this.tail;
        for (let i = 0; i < this.size - index + 1; i++) {
            node = node.prev;
        }
    }
    this.size--;
    node.next = node.next.next;
    node.next.prev = node;
};


206.反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。  

示例 1:

输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]

image.png

思路

这个题写过两三遍了,之前面试也遇到过,前几年还没写出来,感慨自己算法真是太差了,不过现在是掌握了的,这个题两种做法,一种递归实现,一种是迭代实现,都不难,直接上代码。

// 递归算法
var reverseList = function(head) {
    if (!head || !head.next) {
        return head
    }
    const newHead = reverseList(head.next)
    head.next.next = head // 想当年在这个点上愣是没想清楚,😮‍💨,现在是明白了
    head.next = null
    return newHead
};

// 迭代实现
var reverseList = function(head) {
    let pre = null
    let cur = head
    while (cur) {
        let tmp = cur.next
        cur.next = pre
        pre = cur
        cur = tmp
    }
    return pre
        
}

以上题目已上传到github

今日絮叨

上午去开了一个会,耗费一上午的时间,开会内容跟采购软件有关,跟互联网工作是一种很不一样的体验,希望自己能适应。

开完会下午头头就说把采购流程熟悉下吧,然后看文档,感觉自己做了一下午阅读理解,真是不容易,对于高考语文都不高的同学,看这个实在是有点为难😅,不过也算是一种体验。来这边后每一天都是不一样的工作体验,希望自己可以快速成长起来。

看完之后画了一个图给头头,从反馈来看,并不少他想要的,看来我还是理解错了,我应该再多次确认下是要写什么,还是要再多问清楚需求,就像我们写业务需求一样,总是会反复跟pm确认每个细节点,所以这个下次需要改进。

晚上去锻炼了一小时,今天练背,不是很累。晚上写代码感觉身心愉快,尽管比较困了🥱,还是跟代码打交道比较开心😄。