题意分析
- 给出一个链表,要求我们返回这个链表中的所有的元素,但是与NC25不同的是,要求这些元素是在初始的链表里面只出现一次的元素,同时这个链表的所有的元素需要保持原来的相对位置关系。这个就不是去重了。
思路分析
- 我们思考,如何把出现多次的元素在链表里面去掉呢?
- 方法还是利用哈希进行处理,我们用一个哈希表统计这个链表里面的所有的元素出现的次数。处理完之后我们就直到哪些元素需要保留了。
- 其次,我们如何保持原来的顺序呢?
- 保持原来的顺序就要我们按照链表的顺序进行访问。其实这里可以自己重新定义一些新的变量,构造出一个完完全全的新的链表。但是其实没有必要,这样对内存的浪费是很大的。其实我们可以在原来的基础上进行处理,我们只要把一些指针的next指针重新指定就行了。这里我定义了一个pre,用来处理返回答案的那条链表此时所指向的位置,然后用一个now用来进行遍历初始的链表。一旦遇到合适的结点,我就将这个pre指针的next指针指向这个结点,同时pre更新为now,依次下去。但是最后要注意的是我们需要将pre->next置为NULL。确保链表正常终止。但是这个和NC25有点不一样的就是我们的头节点需要我们进行寻找,可能不是初始链表的头节点。这里可能找不到,所以我们直接返回空即可。其次,找到之后就是按照NC25的思路进行查找,和上面的思路一样。
- 具体更新链表请看下面的图进行理解(请原谅我用的是和那题一样的图,因为思路都是一样的,就是处理有点不一样)
- 那么,答题的思路就是我们先进行哈希将这些出现多次的结点排除,然后再进行一次遍历,操作和之前的一样。最后就可以得到最终的链表了。
写法一 C++版
- 代码如下
- 我们只需要对链表进行遍历,所以时间复杂度为O(N)
- 我们需要利用哈希表存储整个链表的所有的元素,所以空间复杂度为O(N)
/** * struct ListNode { * int val; * struct ListNode *next; * }; */ class Solution { public: /** * * @param head ListNode类 * @return ListNode类 */ ListNode* deleteDuplicates(ListNode* head) { // write code here // 特判链表为空的情况,防止出现段错误 if(head==NULL){ return head; } unordered_map<int,int> mp; ListNode* now=head; // 先对链表进行一次遍历哈希出每个数字出现的次数 while(now){ mp[now->val]++; now=now->next; } now=head; // 找到可以作为头节点的第一个结点指针 while(now){ if(mp[now->val]==1){ head=now; break; } now=now->next; } // 注意这里可能找不到,所以需要直接返回空 if(now==NULL){ return NULL; } ListNode* pre=now; // 找到了就用一个pr先存着 now=now->next; // now开始向后面进行遍历 while(now){ // 如果这个数字出现过 if(mp[now->val]==1){ pre->next=now; pre=now; } now=now->next; } // 最后记得结尾指向null pre->next=NULL; return head; } };
写法二 Go语言版
- 代码如下
- 我们只需要对链表进行遍历,所以时间复杂度为O(N)
- 我们需要利用哈希表存储整个链表的所有的元素,所以空间复杂度为O(N)
package main import . "nc_tools" /* * type ListNode struct{ * Val int * Next *ListNode * } */ /** * * @param head ListNode类 * @return ListNode类 */ func deleteDuplicates( head *ListNode ) *ListNode { // write code here if(head==nil){ return nil } pre := new(ListNode) now := new(ListNode) var mp map[int]int mp = make(map[int]int) now=head // 进行一次遍历找到出现多次的数字,排除相关的结点指针 for{ if(now==nil){ break } mp[now.Val]+=1; now=now.Next } now=head // 再进行一次遍历找到头节点,注意不一定是初始链表的第一个节点 for { if(now==nil){ break } if(mp[now.Val]==1){ pre=now break } now=now.Next } // 没有找到头节点,直接返回nil if(now==nil){ return nil } head=pre // pre用来记录最终的链表的尾部结点 now=now.Next for { if(now==nil){ break } // 如果符合题目条件,只出现过一次,那么更新pre指针 if(mp[now.Val]==1){ pre.Next=now pre=now } now=now.Next } // 最后链表尾部指向nil pre.Next=nil return head }