代码随想录算法训练营第三天| 203. 移除链表元素、707. 设计链表、206反转链表

链表理论基础:代码随想录

 203 移除链表元素

 题目链接/文章讲解/视频讲解::代码随想录

 1.代码展现

        方法一:头节点另外处理

		//删除头节点   注意:head != NULL要放在前面
		while (head->val == val && head != NULL) {
			ListNode* temp = head;
			head = head->next;
			delete temp;
		}
		ListNode* cur = head;
		//删除非头节点
		while (cur != NULL && cur->next != NULL) {
			ListNode* temp = cur->next;
			if (temp->val == val) {
				cur->next = cur->next->next;
				delete temp;
			}
			else {
				cur = cur->next;
			}
		}
		//printList(head);
		return head;

        方法二:虚拟头节点处理 

		ListNode* dummyNode = new ListNode();
		dummyNode->next = head;
		ListNode* cur = dummyNode;
		while (cur->next != NULL) {
			ListNode* temp = cur->next;
			if (temp->val == val) {
				cur->next = cur->next->next;
			}
			else {
				cur = cur->next;
			}
		}
		return dummyNode->next;

2.本题小节
        要掌握虚拟头节点的方式,这样每个节点的处理方式都是一样的,该方式需要临时节点指针cur,每次处理的是cur->next下的变量(不能处理cur,如果cur的val是要删除的值,那么不知道cur的上一个指针,没办法处理),因此cur->next!=NULL作为循环条件。
        我这里纠结了一下最后一个节点,cur到了最后一个节点没有进入循环,所以认为最后一个节点没有被处理,实际上在最后一次循环时,最后一个节点的val作为了cur->next的val以及被处理过了。

707设计链表

 题目链接/文章讲解/视频讲解:代码随想录

 1.代码展现
 

class MyLinkedList {
public:
	struct LinkedNode {
	int val;
	LinkedNode* next;
	LinkedNode() : val(0), next(nullptr) {}
	LinkedNode(int x) : val(x), next(nullptr) {}
	LinkedNode(int x, LinkedNode* next) : val(x), next(next) {}
	};
	MyLinkedList() {
		this->mDummyNode = new LinkedNode();
		this->mSize = 0;
	}

	int get(int index) {
		if (index > (mSize - 1) || index < 0) {
			return -1;
		}
		LinkedNode* cur = this->mDummyNode;
		while (index--) {
			cur = cur->next;
		}
		return cur->next->val;
	}

	void addAtHead(int val) {
		LinkedNode* cur = this->mDummyNode;
		LinkedNode* headNode = new LinkedNode(val);
		headNode->next = cur->next;
		cur->next = headNode;
		mSize++;
	}

	void addAtTail(int val) {
		LinkedNode* cur = this->mDummyNode;
		LinkedNode* TailNode = new LinkedNode(val);
		//不是while(mSize--)
		while (cur->next != nullptr) {
			cur = cur->next;
		}
		cur->next = TailNode;
		mSize++;
	}

	void addAtIndex(int index, int val) {
		if (index > mSize) {
			return ;
		}
		if (index < 0) {
			index = 0;
		}
		LinkedNode* cur = this->mDummyNode;
		LinkedNode* addNode = new LinkedNode(val);
		while (index--) {
			cur = cur->next;
		}
		addNode->next = cur->next;
		cur->next = addNode;
		mSize++;
	}

	void deleteAtIndex(int index) {
		if (index > mSize - 1 || index < 0) {
			return;
		}
		LinkedNode* cur = this->mDummyNode;
		while (index--) {
			cur = cur->next;
		}
		LinkedNode* temp = cur->next;
		cur->next = temp->next;
		delete temp;
		temp = nullptr;
		mSize--;
	}

	void printList() {
		LinkedNode* cur = this->mDummyNode;
		while (cur->next != NULL) {
			int val = cur->next->val;
			cout << val << " ";
			cur = cur->next;
		}
		cout << endl;

	}
private:
	int mSize; //长度
	LinkedNode* mDummyNode; //虚拟节点

2.本题小节
        明确需要添加哪些成员,这题添加了长度和虚拟节点,以及链表节点的结构体定义。

        本体使用的是虚拟节点法。构造函数中,长度赋值为0,并且初始化虚拟节点;get()函数中,通过传入的index作为while循环的条件,循环直到找到对应下标下的变量;addAtHead()函数中,正常添加节点即可;addAtTail()函数中,通过循环条件cur->next != nullptr找到链表尾部,再添加节点即可;addAtIndex()函数中,通过传入的index作为while循环条件,找到需要插入的位置插入即可;deleteAtIndex()函数中,通过传入的index作为while循环条件,找到需要删除的位置删除即可。

        需要注意的是链表长度的更新以及每次对传入的index要做判断。

206反转链表

题目链接/文章讲解/视频讲解:代码随想录

 1.代码展现
        方法一:双指针法

		ListNode* pre = nullptr;
		ListNode* cur = head;
		while (cur) {
			//临时储存
			ListNode* temp = cur->next;
			//反转
			cur->next = pre;
			//移动temp和pre
			pre = cur;
			cur = temp;
		}
		return pre;

        方法二:递归法

	ListNode* reserve(ListNode* pre, ListNode* cur) {
		if (cur == nullptr)
			return pre;
		//临时储存
		ListNode* temp = cur->next;
		//反转
		cur->next = pre;
		return reserve(pre, cur);
	}

	ListNode* reverseList(ListNode* head) {
    	//递归解法
		return reserve(nullptr, head);
		
	}

2.本题小节
        思路:创建两个节点指针pre和cur,pre指向为nullptr,cur指向head,反转时,将cur的next指向为pre,这样就完成了反转,反转后,pre变为cur,cur变为cur->next(注意这个节点要提前储存,否则就会取不到),然后再进行下次反转,直到cur指向为nullptr则返回pre作为头节点。

        双指针法与递归法的区别:双指针法在while中进行,以cur != nullptr 为循环条件,在循环中进行反转操作,直到cur = nullptr完成反转;递归是新建了一个函数,传入的参数为每次递归必须要用到的pre和cur结点指针,函数首先判断是否满足返回条件,即cur == nullptr 时,直接返回pre,然后进行反转,并将反转后的pre和cur传入到当前函数中,这样就实现了递归。

        递归一时半会儿理解起来比较困难,但是也可以总结一下递归的共性,首先传入到参数每次都会变换,其次是必须有结束递归的条件,然后是用传入的参数做所需操作,最后是将更新后的参数传入到递归函数中。这样看起来递归确实很像循环,只是每次循环时处理的参数在变,但是它们的处理方法不变,并且达到停止条件时,会将得到的一个返回值一直return到最顶端的递归上,达到递归的目的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值