Chapter 2 | Linked Lists--返回单链表倒数第n个元素及删除中间的某个节点
2013-12-05 13:23
344 查看
2.2 Implement an algorithm to find the nth to last element of a singly linked list.
译文:实现一个算法返回单链表中倒数第n个元素
第一反应是递归,但还是分析一下,单链表返回第n个元素很好办,直接从头结点开始遍历n个。这里返回倒数第n个元素,我们总不能先得出这个链表的元素个数N,然后遍历第N-n+1个吧。这种次序颠倒的问题我们可以想到另一种数据结构——栈,栈有个特点就是先进后出,我们可以遍历一遍链表,将其中的元素逐个压栈,然后再将各个元素弹出来,那么第n个弹出来的元素就是返回值。如果要是再显式地写一个栈来实现的话,就复杂化了,看到栈,我们就得联想到递归,这个自动使用栈的方式,所以我们解决这个问题的第一个方法便是使用递归。
思路比较简单,就是不断遍历,不断“压栈”,遍历完后,就“出栈”,n不断减1,减为0时,此时弹出的元素就是倒数第n个元素。代码如下
除了递归,这里还提供另一个方法,要返回单链表中倒数第n个元素,我们可以借用两个指向链表节点的指针,并使其间隔距离为n-1,将这两个指针同步向链表尾移动,当前面那个移动到尾部时,那么后面那个就是指向倒数第n个链表节点。代码如下
EXAMPLE Input: the node ‘c’ from the linked list a->b->c->d->e Result: nothing is returned, but the new linked list looks likea->b->d->e
译文:实现一个算法来删除单链表的中间结点,只给出指向那个结点的指针
例子 输入:指向链表 a->b->c->d->e 中结点c的指针 结果:无返回值,得到一个新链表 a->b->d->e
这里要清楚的是题目只给出一个指向要删除结点的指针,并没有给出整个链表的头结点指针,所以我们不能直接删除这个结点,否则链表就断了。这是单链表,我们也不能定位被删结点的前面那个结点,所以我们只能从该结点后面的结点做文章,我们只能遍历到被删结点之后的结点。对于链表 a->b->c->d->e,我们仅知道指向c结点的指针,要删除c之后的结点很简单,无论是d还是e,比如删除d,只需node *t = c->next; c->next = t->next; delete t;即可,得到的链表就是 a->b->c->e,与我们预期要得到的链表
a->b->d->e,仅相差一个结点,我们平时基本上都是通过修改结点里面的指针成员指向来修改链表,其实修改里面的数据成员也能达到修改链表的效果,结点结构都是一样的,重要是结点里面的数据。这里我们在上面的基础上就通过修改结点c中的数据成员,将d中的数据传给c就可以了。
c之后 c 本身还是原来的数据,即c还是指向原来的内存地址单元,但是不能访问这个地址单元里面的数据了,这个指针没有关联任何数据,这就是”野指针“,所以在删除指针的时候,通常要将其设置为NULL,这样主要是为在校验的时候有作用。上面嘀咕了一大堆,不知道啰嗦清楚了没。
所以上面直接删除c,然后在打印的时候会出错(我调试运行的时候会出错,原因上面说了)。这里再针对上面不能删除尾结点的情况进行修改,先贴代码
译文:实现一个算法返回单链表中倒数第n个元素
第一反应是递归,但还是分析一下,单链表返回第n个元素很好办,直接从头结点开始遍历n个。这里返回倒数第n个元素,我们总不能先得出这个链表的元素个数N,然后遍历第N-n+1个吧。这种次序颠倒的问题我们可以想到另一种数据结构——栈,栈有个特点就是先进后出,我们可以遍历一遍链表,将其中的元素逐个压栈,然后再将各个元素弹出来,那么第n个弹出来的元素就是返回值。如果要是再显式地写一个栈来实现的话,就复杂化了,看到栈,我们就得联想到递归,这个自动使用栈的方式,所以我们解决这个问题的第一个方法便是使用递归。
思路比较简单,就是不断遍历,不断“压栈”,遍历完后,就“出栈”,n不断减1,减为0时,此时弹出的元素就是倒数第n个元素。代码如下
LINK_NODE *Node = NULL; int n; void findNthToLast(LINK_NODE *pHead) { if (NULL == pHead) return; findNthToLast(pHead->next); --n; if (0 == n) Node = pHead; }递归有时让人摸不着头脑,因为我们有人是通过分析堆栈,分析一个一个函数的调用过程和输出结果来分析递归算法的,简单的还好,要是多几级,这种方式只会把自己弄晕。其实递归本质上也是函数的调用,调用自己的函数和其他函数都是差不多的,没本质区别,函数调用总会将一些临时信息(主要是函数参数和返回值)压栈保存,压栈只是为了函数能够正确的返回,我们使用递归时,我们感兴趣的信息变量(递归时一般就是函数形参和返回值)就在不断地压栈出栈,所以我们在理解递归的时候可结合栈这一数据结构来分析。递归运行时可以概括为“能进则进,不进则退”,进的时候压栈,进不了我就退,就不断的出栈。所以递归的效率一直被人诟病,但我们不能忘记它的好,可正常使用的递归必须要有终止条件,不能无限制的递归下去,不然最后就是栈溢出了。
除了递归,这里还提供另一个方法,要返回单链表中倒数第n个元素,我们可以借用两个指向链表节点的指针,并使其间隔距离为n-1,将这两个指针同步向链表尾移动,当前面那个移动到尾部时,那么后面那个就是指向倒数第n个链表节点。代码如下
LINK_NODE* findNthToLast(LINK_NODE *pHead, int n) { if ((NULL == pHead) || (n < 1)) return NULL; LINK_NODE *ptr_front = pHead, *ptr_behind = pHead; for (int i = 0; i < n; ++i) { if (NULL == ptr_front) return NULL; //表明链表的长度小于n ptr_front = ptr_front->next; } while (ptr_front != NULL) { ptr_front = ptr_front->next; ptr_behind = ptr_behind->next; } return ptr_behind; }2.3 Implement an algorithm to delete a node in the middle of a single linked list, given only access to that node.
EXAMPLE Input: the node ‘c’ from the linked list a->b->c->d->e Result: nothing is returned, but the new linked list looks likea->b->d->e
译文:实现一个算法来删除单链表的中间结点,只给出指向那个结点的指针
例子 输入:指向链表 a->b->c->d->e 中结点c的指针 结果:无返回值,得到一个新链表 a->b->d->e
这里要清楚的是题目只给出一个指向要删除结点的指针,并没有给出整个链表的头结点指针,所以我们不能直接删除这个结点,否则链表就断了。这是单链表,我们也不能定位被删结点的前面那个结点,所以我们只能从该结点后面的结点做文章,我们只能遍历到被删结点之后的结点。对于链表 a->b->c->d->e,我们仅知道指向c结点的指针,要删除c之后的结点很简单,无论是d还是e,比如删除d,只需node *t = c->next; c->next = t->next; delete t;即可,得到的链表就是 a->b->c->e,与我们预期要得到的链表
a->b->d->e,仅相差一个结点,我们平时基本上都是通过修改结点里面的指针成员指向来修改链表,其实修改里面的数据成员也能达到修改链表的效果,结点结构都是一样的,重要是结点里面的数据。这里我们在上面的基础上就通过修改结点c中的数据成员,将d中的数据传给c就可以了。
bool deleteNode(LINK_NODE *c) { if ((NULL == c) || (NULL == c->next)) return false; //没有考虑被删结点为尾结点的情况 LINK_NODE *t = c->next; c->data = t->data; c->next = t->next; delete t; return true; }这是利用被删除结点后面的那个结点来进行处理的,那如果被删除的结点恰是最后一个尾结点呢,由于其后面没有结点,我们就不能采用这种方法来进行处理。要是直接删除尾结点c会怎样勒,我们要知道 delete c 或 free (c) 并不是真正意义的完全删除那个结点(其他也是一样),只是一个”解绑的关系“,就是解除c 这个指针与它指向的内存地址数据之间的关系,一旦解除了,c 对它所指向的内存就没有使用权了,这块内存就可以被别人使用了(不是说别人可以用里面的数据),这就是内存释放,不过这个内存地址里面的数据还是原来c关联的数据,但跟c没有半毛钱关系了,当然其实跟谁也没有关系,因为谁也找不到(没有指针与它相关联,你申明一个变量它也会有地址,世界是物质的),只有重新分配(系统和手动)的时候,用到了这个内存的时候,这个内存就可以重新使用了。delete
c之后 c 本身还是原来的数据,即c还是指向原来的内存地址单元,但是不能访问这个地址单元里面的数据了,这个指针没有关联任何数据,这就是”野指针“,所以在删除指针的时候,通常要将其设置为NULL,这样主要是为在校验的时候有作用。上面嘀咕了一大堆,不知道啰嗦清楚了没。
所以上面直接删除c,然后在打印的时候会出错(我调试运行的时候会出错,原因上面说了)。这里再针对上面不能删除尾结点的情况进行修改,先贴代码
bool deleteNode(LINK_NODE **pNode) { if ((NULL == *pNode)) return false; if (NULL == (*pNode)->next) { delete (*pNode); *pNode = NULL; return true; } LINK_NODE *t = (*pNode)->next; (*pNode)->data = t->data; (*pNode)->next = t->next; delete t; return true; }函数形参是对其实参进行了一份拷贝,内容一样,但是地址不一样,所以需要指针来处理,本身是指针,就是指针的指针了。这里根据题目情况适当进行了修改,在主程序中在这样申明调用即可
LINK_NODE **pNode = &c ; //这里c为给定的链表中要被删除的结点 if (deleteNode(pNode)) { PrintLinkNode(pLinkNode); } else cout << "failure" << endl;既然用了双指针,那么也可用指针的引用来实现
bool deleteNode(LINK_NODE *&pNode) { if ((NULL == pNode)) return false; if (NULL == pNode->next) { delete (pNode); pNode = NULL; return true; } LINK_NODE *t = pNode->next; pNode->data = t->data; pNode->next = t->next; delete t; return true; }注意的是,在主程序中调用的时候,直接用给定的c结点作为参数传入,不要通过声明另一个变量传入,不然引用的就不是c了。
相关文章推荐
- 返回单链表的倒数第n个节点
- 008实现一个算法从一个单链表中返回倒数第n个元素(keep it up)
- 删除单链表的倒数第N个节点
- 一个单链表中返回倒数第n个元素
- 给定一个链表,删除链表中倒数第n个节点,返回链表的头节点。
- leetcode_19. Remove Nth Node From End of List 删除单链表中的倒数第n个节点,双指针法
- 坚持坚持!用Java写出删除一个链表的倒数第N个节点,并返回头节点(N总是可达的)
- cc150:实现一个算法从一个单链表中返回倒数第n个元素
- 删除单链表的倒数第n个元素
- 删除单链表倒数第n个节点
- 删除单链表倒数第n个节点
- [LintCode] Delete Node in the Middle of Singly Linked List 在单链表的中间删除节点
- 单链表的创建,表长,插入,查找,逆置,中间元素,删除节点,打印
- 008实现一个算法从一个单链表中返回倒数第n个元素(keep it up)
- 删除单链表的头元素;单链表,只是遍历一次,求出中间节点
- 删除单链表中的倒数第n个节点的实现及测试程序
- 2.2 实现一个算法从一个单链表中返回倒数第n个元素
- 线性表---单链表(创建、找中间节点、删除头元素)
- 删除链表中倒数第n个节点 - C++
- [C++]LeetCode 19: Remove Nth Node From End of List(删除链表中倒数第n个节点)