算法训练营 第三天

算法训练营 第三天

今天内容:链表01

链表基础

链表和数组的区别在于链表的内存是分开存储的,数组是连续的内存,因此数组的查找时间复杂度为O(1),链表需要从头开始找,查找时间复杂度为O(n),数组删除增加元素需要重新找一块内存空间,新建一个数组,将原数组搬过去,所以增删时间复杂度为O(n),链表直接改改指针,原地删除就行,所以增删时间复杂度为O(1).

203.移除链表元素

移除链表元素有两种方式,一种是用原链表操作,这种方式需要特殊处理head节点,因为head节点的处理方式和其他节点不一样,head的处理如下所示

while(head->val==val){
	listNode* tmp = head;
	head = head->next;
	delete tmp;
}

第二种方法是添加一个虚拟头节点,这种方法可以保证head的处理方式和其他节点一样了,需要注意的是,这种方法返回时需要返回虚拟头节点的next(dH->next),因为原头节点可能被释放了。

listNode* dH = new listNode(0,head);
listNode* cur = head;
while(cur!=NULL && cur->next!=NULL){
	if(cur->next->val==val){
		listNode* tmp = cur->next;
		cur->next = cur->next->next;
		delete tmp;
	}
	else{
		cur = cur->next;
	}
}
return dH->next;

707. 设计链表

这道题用虚拟头节点是最好的,虚拟头节点的代码已经有很多了,我写了用原链表的设计方法

class MyLinkedList {
public:
    MyLinkedList() {
        head = NULL;
        size = 0;
    }

    int get(int index) {
        if (index >= size)
            return -1;
        ListNode* cur = head;
        while (index--) {
            cur = cur->next;
        }
        return cur->val;
    }

    void addAtHead(int val) {
        ListNode* newHead = new ListNode(val, head);
        head = newHead;
        size++;
        return;
    }

    void addAtTail(int val) {
        if (size == 0) {
            ListNode* newNode = new ListNode(val, NULL);
            head = newNode;
            size++;
            return;
        }

        ListNode* cur = head;
        int index = size - 1;
        while (index--) {
            cur = cur->next;
        }
        ListNode* newNode = new ListNode(val, NULL);
        cur->next = newNode;
        size++;
        return;
    }

    void addAtIndex(int index, int val) {
        if (index == size) {
            addAtTail(val);
            return;
        }
        if (index > size) {
            return;
        }
        if (index == 0) {
            addAtHead(val);
            return;
        }
        ListNode* newNode = new ListNode(val, NULL);
        ListNode* cur = head;
        index--;
        while (index--) {
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = newNode;
        newNode->next = tmp;
        size++;
        return;

    }

    void deleteAtIndex(int index) {
        if (index >= size||index < 0)
            return;
        if (index == 0) {
            ListNode* tmp = head;
            head = head->next;
            delete tmp;
            size--;
            return;
        }
        ListNode* cur = head;
        index--;
        while (index--) {
            cur = cur->next;
        }
        ListNode* tmp = cur->next;
        cur->next = cur->next->next;
        delete tmp;
        size--;
        return;
    }
private:
    ListNode* head;
    int size;
};

这道题用原链表的方法需要做很多特殊情况处理,容易忽略一些情况,很难一次ac,在题目没有强调不能用虚拟头节点时还是用虚拟头节点方便一些,但原链表的方式也要会(之前参加某行笔试有要求不让用虚拟头节点)。

206. 反转链表

来了!经典的反转链表题,面试时被问烂了,hr的暖场神器!
这道题在两个月前刷过,但现在再看还是有些生疏,我将双指针法和递归法都实现了一遍,这道题怎么想到用双指针呢,因为这是单链表,要是反转链表肯定不能从后往前处理(后边的节点是无法得到前面节点信息的),从前往后的的话,就类似头插的方法完成反转(得到的第一个节点是尾节点,后边得到的所有节点都从前面插进新链表)
写完这道题,对递归的思想理解更深了一些,得注意return的是什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值