由于之前看了牛客网的数据结构和算法的课程知道了左神,现在找到了这本书当作入门书做做吧,虽然书的题解都是java实现的,但好在用c++实现难度不大
第二章 链表问题
第一题:删除无序单链表中值重复出现的节点
方法一:用哈希表,时间复杂度O(N),额外空间复杂度O(N)
方法二:类似选择排序的过程,时间复杂度O(N^2),额外空间复杂度O(1)
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
void removeRep2(ListNode* head) {
ListNode* cur=head,*next=head->next,*pre=head;//不用先记录next,先删完再直接cur=cur->next
while (cur)
{
while (next)
{
if (cur->val==next->val)
{
pre->next=next->next;
}
else
{
pre=next;
}
next=next->next;
}
cur=cur->next;
}
}
};
题目二:在单链表中删除指定值的节点
方法一:利用栈或者其他容器收集节点的方法,时间复杂度O(N),额外空间复杂度O(N)
方法二:不用任何容器直接调整,时间复杂度O(N),额外空间复杂度O(1)
class Solution {
public:
ListNode* removeVal(ListNode* head, int num) {
ListNode*cur=head,*pre=head,*next=head->next;
while(cur->val==num&&cur==head)
{
head=next;
cur=head;
next=next->next;
}
pre=head;
cur=next;
next=next->next;
while (cur)
{
if (cur->val==num)
{
pre->next=next;
}
else{
pre=cur;
}
cur=cur->next;
next=next->next;
}
return head;
}
}
第三题:单链表的选择排序
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
ListNode* getSmallestPreListNode(ListNode* head){
ListNode* smallpre=nullptr,*pre=head,*cur=head->next,*small=head;//需要一个small来记录最小值
while (cur)
{
if (cur->val<small->val)
{
smallpre=pre;
small=cur;
}
pre=cur;
cur=cur->next;
}
return smallpre;
}
ListNode* reverseKGroup(ListNode* head) {
ListNode *tail=nullptr,*cur=head,*small=nullptr,*smallpre=nullptr;
while (cur)
{
small=cur;//这样如果每一次遍历发现cur->val就是最小值,也就是getSmallestPreListNode返回nullptr,那么接下来的操作也可以顺利把cur->val放进有序链表中
smallpre=getSmallestPreListNode(cur);
if (smallpre!=nullptr)
{
small=smallpre->next;
smallpre->next=smallpre->next->next;
}
cur=cur==small?cur->next:cur;
//这个ifelse加tail=small完美将新的头结点的创建和尾巴的更新实现了
if (tail==nullptr)
{
head=small;
}
else
{
tail->next=small;
}
tail=small;
}
return head;
}
}
第四题:一种怪异的节点删除方式
举例:链表1-2-3-null,只知道要删除节点2,而不知道头结点。
只需要把2节点变为3,然后删除3即可。
问题一:这样的删除方式无法删除最后一个节点,如果是节点3的话,我们能不能把节点3在内存上的区域变成nullptr呢?这样不就相当于让节点2的next指针指向了nullptr,起到节点3被删除的效果了吗?不可以。nullptr在系统中是一个特定的区域,如果想让节点2的next指针指向nullptr,必须找到节点2.
问题二:这种删除方式在本质上根本就不是删除了node节点,而是把node节点的值改变,然后删除node的下一个节点,在实际的工程中可能会带来很大问题。比如,工程上的一个节点可能代表很复杂的结构,节点值的复制相当复杂,或者可能改变节点值这个操作都是被禁止的;再如,工程上的一个节点代表提供服务的服务器,外界对每个节点都有很多依赖,比如,实例中删除节点2时,其实影响了节点3对外提供的服务。
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
int error(const char a[]) {
std::cout << a;
}
void removeListNodeWired(ListNode* head) {
if (head == nullptr)return;
ListNode* next = head->next;
if (next == nullptr)
throw error("can not remove last code");
head->val = next->val;
head->next = next->next;
}
};