前言:
从今天开始要攻克算法专题了,今天是链表篇,关于链表相关的考题,不会太多涉及时间复杂度,而主要考察链表和指针操作;为啥大厂喜欢考察数据结构和算法?因为这些是对基本功的升华,不会考察数组指针、函数指针等,考一个链表,就能考察对指针的理解,我相信不理解指针,链表学起来很费劲!
一、简介
我会一个模块一个模块进行学习和练习,练习时我会从leetcode上选题,都知道leetcode吧?是OJ中最权威的平台了,在上面可以找算法题和练习,很好的一个网站,每一个题都会说明leetcode的第几题,方便大家查找和练习。
二、反转链表
LeetCode上第206题:Reverse Linked List,官网是英文,但鉴于英文对一些人看起来比较费劲,翻译成中文,如下:
反转单链表。 例子: 输入:1 - > 2 - > 3 - > 4 - > 5 - > NULL 输出:5 - > 4 - > 3 - > 2 - > 1 - > NULL 跟进: 链表可以迭代或递归地反转。你能实现这两个吗?
可能有人会想到直接去改链表节点里的值,这是不允许,一般都是操作next指针,去改变指针指向;画图进行讲解,如下:
需要三个指针pre/cur/next去反转,将2位置指向pre位置,pre指向1号位置,1号位置指向3号位置,这样就可以进行反转了。代码如下:
struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; // 206. Reverse Linked List // https://leetcode.com/problems/reverse-linked-list/description/ // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* pre = NULL; ListNode* cur = head; while(cur != NULL){ ListNode* next = cur->next; cur->next = pre; pre = cur; cur = next; } return pre; } };
我是按照LeetCode的格式进行编写的,然后去LeetCode上去试一下,鉴于可能有人不知道怎么使用LeetCode,我简单进行演示一下怎么使用:
第一步:百度leetcode,如下:
第二步:点击“Create Account”,创建自己的用户,需要填写邮箱,需要点击链接进行激活,否则刷题无法提交;刚开始写的邮箱没给我发邮件,又在个人资料里重新换了邮箱,就可以收到了;
第三步:在首页找题,如果能记住题目,可以输入题目进行搜索;也可以搜索题号,如206,也可以搜索到,如下图:
第四步:提交代码,我将上面写的代码放到leetcode,点击右下角的“Submit Solution”,就可以看到下面的“Submission Solution:Accepted”,就表示通过了,如下图:
这样就OK了。
三、测试程序
这部分主要说明一下怎么去自己测试程序的运行?主要实现链表的创建、遍历、销毁(C++堆上内存要自己管理)。
1、创建链表
将数组传给函数,根据数组实现链表赋值;还会传入n创建多大的链表,代码如下:
// 根据n个元素的数组arr创建一个链表, 并返回链表的头 ListNode* createLinkedList(int arr[], int n){ if(n == 0) return NULL; ListNode* head = new ListNode(arr[0]); ListNode* curNode = head; for(int i = 1 ; i < n ; i ++){ curNode->next = new ListNode(arr[i]); curNode = curNode->next; } return head; }
注意:创建的链表,没有真实的“头结点”,就是只存一个开始指针的节点,所以删除第一个节点要注意!
2、遍历链表
通过头结点进行遍历链表,代码如下:
// 打印以head为头结点的链表信息内容 void printLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ cout << curNode->val << " -> "; curNode = curNode->next; } cout << "NULL" << endl; return; }
3、销毁链表
将创建时分配的内存释放,代码如下:
// 释放以head为头结点的链表空间 void deleteLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ ListNode* delNode = curNode; curNode = curNode->next; delete delNode; } return; }
4、测试程序
对反转链表代码进行测试,整体代码如下:
#includeusing namespace std; /** * Definition for singly-linked list. */ struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; /// LinkedList 测试辅助函数 // 根据n个元素的数组arr创建一个链表, 并返回链表的头 ListNode* createLinkedList(int arr[], int n){ if(n == 0) return NULL; ListNode* head = new ListNode(arr[0]); ListNode* curNode = head; for(int i = 1 ; i < n ; i ++){ curNode->next = new ListNode(arr[i]); curNode = curNode->next; } return head; } // 打印以head为头结点的链表信息内容 void printLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ cout << curNode->val << " -> "; curNode = curNode->next; } cout << "NULL" << endl; return; } // 释放以head为头结点的链表空间 void deleteLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ ListNode* delNode = curNode; curNode = curNode->next; delete delNode; } return; } // 206. Reverse Linked List // https://leetcode.com/problems/reverse-linked-list/description/ // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* pre = NULL; ListNode* cur = head; while(cur != NULL){ ListNode* next = cur->next; cur->next = pre; pre = cur; cur = next; } return pre; } }; int main(){ int arr[] = {1, 2, 3, 4, 5}; int n = sizeof(arr)/sizeof(int); ListNode* head = createLinkedList(arr, n); printLinkedList(head); ListNode* head2 = Solution().reverseList(head); printLinkedList(head2); deleteLinkedList(head2); return 0; }
运行结果如下:
进行了反转,没有问题;
四、删除链表元素
1、题目
LeetCode上第203题目:Remove Linked List Elements,题目如下:
从具有值val的整数链表中删除所有元素。 例子: 输入:1->2->6->3->4->5->6,val = 6 输出:1 - > 2 - > 3 - > 4 - > 5
2、分析题目
先来分析一下题目,用图来解释如下:
假如删除值为4的节点,先把4的next指针保存,在3号位置指向5,这完全没有问题;但问题会发生在第一个节点位置,它没有前一个节点,那怎么办呢?在前面创建链表时也说过:没有头结点,所以使用虚拟头结点!
代码如下:
// 203. Remove Linked List Elements // https://leetcode.com/problems/remove-linked-list-elements/description/ // 使用虚拟头结点 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution { public: ListNode* removeElements(ListNode* head, int val) { // 创建虚拟头结点 ListNode* dummyHead = new ListNode(0); dummyHead->next = head; ListNode* cur = dummyHead; while(cur->next != NULL){ if(cur->next->val == val){ ListNode* delNode = cur->next; cur->next = delNode->next; delete delNode; } else cur = cur->next; } ListNode* retNode = dummyHead->next; delete dummyHead; return retNode; } };
3、测试
测试程序也是上面的链表创建和遍历,程序如下:
#includeusing namespace std; ///Definition for singly-linked list. struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; /// LinkedList Test Helper Functions ListNode* createLinkedList(int arr[], int n){ if(n == 0) return NULL; ListNode* head = new ListNode(arr[0]); ListNode* curNode = head; for(int i = 1 ; i < n ; i ++){ curNode->next = new ListNode(arr[i]); curNode = curNode->next; } return head; } void printLinkedList(ListNode* head){ if(head == NULL){ cout << "NULL" << endl; return; } ListNode* curNode = head; while(curNode != NULL){ cout << curNode->val; if(curNode->next != NULL) cout << " -> "; curNode = curNode->next; } cout << endl; return; } void deleteLinkedList(ListNode* head){ ListNode* curNode = head; while(curNode != NULL){ ListNode* delNode = curNode; curNode = curNode->next; delete delNode; } return; } // 203. Remove Linked List Elements // https://leetcode.com/problems/remove-linked-list-elements/description/ // 使用虚拟头结点 // 时间复杂度: O(n) // 空间复杂度: O(1) class Solution { public: ListNode* removeElements(ListNode* head, int val) { // 创建虚拟头结点 ListNode* dummyHead = new ListNode(0); dummyHead->next = head; ListNode* cur = dummyHead; while(cur->next != NULL){ if(cur->next->val == val){ ListNode* delNode = cur->next; cur->next = delNode->next; delete delNode; } else cur = cur->next; } ListNode* retNode = dummyHead->next; delete dummyHead; return retNode; } }; int main() { int arr[] = {1, 2, 6, 3, 4, 5, 6}; int n = sizeof(arr) / sizeof(int); ListNode* head = createLinkedList(arr, n); printLinkedList(head); Solution().removeElements(head, 6); printLinkedList(head); deleteLinkedList(head); return 0; }
运行结果如下:
总结:
希望通过这篇博客,大家能对基本的链表算法题能轻松应对;欢迎点赞,不懂的欢迎随时评论!多多支持,谢谢!