递归地从链表中删除数据
Posted
技术标签:
【中文标题】递归地从链表中删除数据【英文标题】:Deleting data from a linked list recursively 【发布时间】:2018-01-23 23:57:04 【问题描述】:我有一个链表定义为
struct Node
int data;
Node *next;
;
struct LinkedList
Node *head;
;
我想递归地遍历我的链表并删除具有指定数据类型的节点(并正确地重新加入节点)。我想出了如何迭代地做到这一点,但我正在努力递归地做到这一点。这是我到目前为止得到的:
void deleteNodeRecursively(LinkedList* list, int value)
if (list->head==NULL)
return;
else if (list->head->data==value)
list->head=list->head->next;
deleteNodeRecursively(list,value);
else
基本上,我的策略是确定头部是否有数据。如果是这样,那么我只需将头部替换为下一个节点。问题是我的 else 语句,我知道我必须“移动”到下一个节点。我不仅要移动到下一个节点,而且我必须确保它是 LinkedList 格式,这样我才能正确使用头部。我不知道如何在不删除所有列表的情况下继续前进。我对制作副本有模糊的想法?我不太确定现在该做什么。
编辑:我宁愿不编辑我的结构定义,因为我将它们用于我的其他程序。
【问题讨论】:
离题:看起来deleteNodeRecursively
应该是LinkedList
成员函数
关于主题,您需要在其中进行一些抽象。 void deleteNodeRecursively(LinkedList* list, int value)
不应该是递归的。它应该调用递归的deleteNextNode(Node* node, int value)
。您还需要添加一些额外的簿记,例如传递对 prev
节点的引用,以便您知道要附加到什么。
你不应该在链表上使用递归。你应该使用迭代。不要对迭代问题使用递归。用它来解决递归问题。
学习使用调试器也可能是一个好主意,因为您可以更轻松地找出不工作的地方,然后编写工作的代码。
@EJP - 链表是一种递归数据结构,为什么不使用递归呢?
【参考方案1】:
有很多方法可以做到。
我个人遵循一种简单的方法,即在调用递归函数之前处理签入头。
if(list->head->data == value)
Node* temp = list->head;
list->head = list->head->next;
delete temp;
if(list->head != null)
deleteNodeRecursively(list->head,list->head->next,value);
你的递归函数应该是这样的,
void deleteNodeRecursively(Node* parent, Node* child, int value)
if (child == NULL) // if null that means end of the list
return;
if( child->data == value)
parent->next = child->next; // attach the parent with the next of child. Thus removes the child form the list
delete child; // clear the dynamically allocated memory
deleteNodeRecursively(parent,parent->next,value);
else
deleteNodeRecursively(child,child->next,value); // move to the next
附加如果可以避免递归,请避免递归。因为如果列表足够长,它会在到达末尾之前消耗你的全部堆栈内存。
【讨论】:
使用尾递归,列表长度没有堆栈含义。【参考方案2】:我正在努力递归地执行此操作。 我的策略是判断头部是否有数据
一个很好的策略,类似于 MASh 提案,下面是我的努力。
也许以下 removeR() 方法将有助于完成你的。
在这个例子中我没有提供“尾递归”。以下方法是我的“传统”,即简单的实现。 (最近我发现我很少使用列表,更喜欢 std::vector。)
环境:
Ubuntu 15.10,64 位,在旧版戴尔上,
g++-5 (Ubuntu 5.2.1-23ubuntu1~15.10) 5.2.1 20151028
代码:
#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point Time_t; // std-chrono-hi-res-clk-time-point
typedef std::chrono::milliseconds MS_t; // std-chrono-milliseconds
typedef std::chrono::microseconds US_t; // std-chrono-microseconds
typedef std::chrono::nanoseconds NS_t; // std-chrono-nanoseconds
using namespace std::chrono_literals; // support suffixes like 100ms, 2s, 30us
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <cassert>
class Node_t
private:
size_t m_id; // node indx
int m_data;
Node_t* m_next;
static size_t M_id;
public:
Node_t() = delete; // disallow default ctor
Node_t(int d) : m_id(++M_id) , m_data(d) , m_next(nullptr)
~Node_t() std::cout << " ~Node_t() " << show() << std::endl;
void appendR(Node_t* n)
if(m_next) m_next->appendR(n); // spin to end of list
else m_next = n; // append to end
bool dataMatch(int v) return (m_data == v);
Node_t* next() return (m_next);
// when m_next->m_data matches v, returns true, else false
bool removeR(int v);
std::string showR()
std::string s = show();
if(m_next)
s += m_next->showR(); // spin to end of list
return (s);
std::string show()
std::stringstream ss;
if (false) ss << "[" << std::to_string(m_id) << "]"; // node indx
if (false) ss << std::to_string(m_id) << ":"; // node indx
ss << std::to_string(m_data) << " ";
return ss.str();
size_t size()
size_t retVal = 1;
if (m_next) retVal += m_next->size(); // spin to end of list
return retVal;
; // class Node_t
size_t Node_t::M_id = 0; // unique id for each node
class LinkedList_t
private:
Node_t* m_head;
public:
LinkedList_t() : m_head (nullptr)
~LinkedList_t() = default;
void append(Node_t* n)
assert(n);
if (nullptr == m_head) m_head = n;
else m_head->appendR(n);
void removeR(int v);
std::string show()
std::string s("ll is empty");
if(m_head)
s = m_head->showR();
return (s);
size_t size()
size_t retVal = 0;
if (m_head) retVal += m_head->size();
return retVal;
; // class LinkedList_t
void LinkedList_t::removeR(int v)
if(nullptr == m_head) // empty list check
std::cout << "\n empty" << std::endl;
return;
if(m_head->dataMatch(v)) // check data at head
Node_t* tmp = m_head;
m_head = tmp->next(); // unlink prev head
delete tmp;
removeR(v); // local recursion... node 2 might also contain v
// not empty && data at head not v then use node recursion
if(m_head) (void)m_head->removeR(v);
bool Node_t::removeR(int v)
bool match = false;
if (m_next) match = m_next->removeR(v); // spin to end of list
// during decurse
if(match) // m_next->data matches v
Node_t* matchNode = m_next; // node-to-remove
m_next = m_next->m_next; // remove link to node-to-remove
delete matchNode; // remove node
// else no remove to do
return (m_data == v); // continue decurse, checking for another match
class T516_t
private:
LinkedList_t m_ll;
public:
T516_t() = default;
~T516_t() = default;
int exec()
show("m_ll.show().1");
std::cout << "\n m_ll.size() = " << m_ll.size() << std::endl;
for(int i=0; i<6; ++i)
Node_t* n = new Node_t(i+10); assert(n); // 10..15
m_ll.append(n);
for(int i=0; i<6; ++i)
Node_t* n = new Node_t(i+10); assert(n); // 10..15
m_ll.append(n);
;
show("m_ll.show().2");
std::cout << "\n m_ll.size() = " << m_ll.size() << std::endl;
show("remove(15)");
m_ll.removeR(15);
show("m_ll.show().3");
show("remove(10)");
m_ll.removeR(10);
show("m_ll.show().z");
for (int i=0; i<6;++i)
std::cout << "\n remove(" << i+10 << ") " << std::endl;
m_ll.removeR(i+10);
show("m_ll.show()...");
return(0);
private:
void show(std::string lbl)
std::cout << "\n " << lbl
<< " sz:" << m_ll.size()
<< " " << m_ll.show() << std::endl;
; // class T516_t
int main(int , char** )
Time_t start_us = HRClk_t::now();
int retVal = -1;
T516_t t516;
retVal = t516.exec();
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
std::cout << "\n\n FINI " << duration_us.count() << " us" << std::endl;
return(retVal);
结果:
R02: dumy524.cc
rm -f dumy524
g++-5 -m64 -O3 -ggdb -std=c++14 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic -Wcast-align -Wcast-qual -Wconversion -Wpointer-arith -Wunused -Woverloaded-virtual -O0 dumy524.cc -o dumy524 -L../../bag -lbag_i686
real 0m1.065s
user 0m0.920s
sys 0m0.100s
m_ll.show().1 sz:0 ll is empty
m_ll.size() = 0
m_ll.show().2 sz:12 10 11 12 13 14 15 10 11 12 13 14 15
m_ll.size() = 12
remove(15) sz:12 10 11 12 13 14 15 10 11 12 13 14 15
~Node_t() 15
~Node_t() 15
m_ll.show().3 sz:10 10 11 12 13 14 10 11 12 13 14
remove(10) sz:10 10 11 12 13 14 10 11 12 13 14
~Node_t() 10
~Node_t() 10
m_ll.show().z sz:8 11 12 13 14 11 12 13 14
remove(10)
m_ll.show()... sz:8 11 12 13 14 11 12 13 14
remove(11)
~Node_t() 11
~Node_t() 11
m_ll.show()... sz:6 12 13 14 12 13 14
remove(12)
~Node_t() 12
~Node_t() 12
m_ll.show()... sz:4 13 14 13 14
remove(13)
~Node_t() 13
~Node_t() 13
m_ll.show()... sz:2 14 14
remove(14)
~Node_t() 14
~Node_t() 14
empty
m_ll.show()... sz:0 ll is empty
remove(15)
empty
m_ll.show()... sz:0 ll is empty
FINI 591 us
【讨论】:
"Node_t::m_id" 仅用于诊断,我有意将其包括在内以提供诊断支持。删除它应该没有问题,因为它仅在我的“show()”方法中使用。 上面提供的方法“removeR()”可能被称为“removeAll()”。其他语义选择应该源自您的应用程序的需求。它们可能包括:removeFirst() [fifo]、removeLast() [lifo]、removeLT() [以上是关于递归地从链表中删除数据的主要内容,如果未能解决你的问题,请参考以下文章