数据结构之链表-链表实现及常用操作(C++篇)

0.摘要

  • 定义
  • 插入节点(单向链表)
  • 删除节点(单向链表)
  • 反向遍历链表
  • 找出中间节点
  • 找出倒数第k个节点
  • 翻转链表
  • 判断两个链表是否相交,并返回相交点
  • 判断链表是否有环路,获取连接点,计算环的长度
  • 二叉树和双向链表转化

1.定义

1.1单向链表

单向链表的节点包括:

  1. 数据域:用于存储数据元素的值。

  2. 指针域(链域):用于存储下一个结点地址或者说指向其直接后继结点的指针。

     struct Node{
    int value;
    Node * next;
    };

1.2双向链表

双向链表的节点包括:

  1. 数据域:用于存储数据元素的值。

  2. 左指针域(左链域):用于存储上一个结点地址或者说指向其直接前继结点的指针。

  3. 右指针域(右链域):用于存储下一个结点地址或者说指向其直接后继结点的指针。

     struct DNode{
    int value;
    DNode * left;
    DNode * right;
    };

2.常用操作例题

2.1插入节点(单向链表)

//p节点后插入值为i的节点
void insertNode(Node *p, int i){
Node* node = new Node;
node->value = i;
node->next = p->next;
p->next = node;
}

2.2删除节点(单向链表)

当需要删除一个节点p时,只需要将p的前一个节点的next赋为p->next,但是由于是单向的,只知道p,无法知道p前一个节点,所以需要转换思路。将p下一个的节点覆盖到p节点即可,这样就等效于将p删除了。

void deleteNode(Node *p){
p->value = p->next->value;
p->next = p->next->next;
}

2.3反向遍历链表

法一:反向遍历链表就类似于事先遍历的节点后输出,即“先进后出”,那么可以将链表遍历存放于栈中,其后遍历栈依次弹出栈节点,达到反向遍历效果。

//1.stack
void printLinkedListReversinglyByStack(Node *head){
stack<Node* > nodesStack;
Node* pNode = head;
//遍历链表
while (pNode != NULL) {
nodesStack.push(pNode);
pNode = pNode->next;
}
while (!nodesStack.empty()) {
pNode=nodesStack.top();
printf("%d\t", pNode->value);
nodesStack.pop();
}
}
//2.递归
void printLinkedListReversinglyRecursively(Node *head){
if (head!=NULL) {
if (head->next!=NULL) {
printLinkedListReversinglyRecursively(head->next);
}
printf("%d\t", head->value);
}
}

2.4找出中间节点

用slow和fast指针标记,slow每次走一步,fast每次走两步,当fast到尾节点时,slow就相当于总长度的一半,即在中间节点。

//找出中间节点
Node* findMidNode(Node* head){
Node* slow = head;
Node* fast = head;
while (fast->next != 0&&fast->next->next!=0) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}

2.5找出倒数第k个节点

用slow和fast指针标记,fast指针事先走k步,然后slow和fast同时走,当fast到达末节点时,slow在fast的前k个节点,即为倒数第k个节点。

//找出倒数第k个节点
Node* findKNode(Node* head,int k){
Node *temp1 = head;
Node *temp2 = head;
while (k-->0) {
if(temp2 == NULL){
return NULL;
}
temp2 =temp2->next;
}
while (temp2->next != NULL&&temp2->next->next!=NULL) {
temp1 = temp1->next;
temp2 = temp2->next->next;
}
return temp1;
}

2.6翻转链表

题意即为将链表反过来,即,原本为p1-p2-p3翻转为p3-p2-p1。读者需自行画图体会指针操作。

//翻转链表
Node * reverseLinkedList(Node* head,int k){
Node *reversedHead = NULL;
Node *pNode = head;
Node *pre = NULL;
while (pNode!=NULL) {
if (pNode->next==NULL) {
reversedHead = pNode;
}
Node* nxt = pNode->next;
pNode->next = pre;
pre=pNode;
pNode=nxt;
}
return reversedHead;
}

2.7判断两个链表是否相交,并返回相交点

如果两个链表相交,其形状必为y形,而不可以能为x形,即两条链表必有相同的尾节点。首先,计算得到两个链表的长度:m,n,求得两个链表长度之差distance=|m-n|,让较长得那个链表事先走distance步,这样,若是链表相交得话,二者指针必相撞,相撞点即为相交点。

Node* findCrosspoint(Node* l1, Node* l2){
int m = getLinkedListLength(l1);
int n = getLinkedListLength(l2);
int distance=0;
Node *temp1= l1;
Node *temp2= l2;
if (m>n) {
distance = m - n;
while (distance>0) {
distance--;
temp1=temp1->next;
}
} else{
distance = n - m;
while (distance>0) {
distance--;
temp2 = temp2->next;
}
}
while(temp1!=temp2&&temp1->next!=NULL&&temp2->next!=NULL){
temp1=temp1->next;
temp2=temp2->next;
}
if(temp1 == temp2){
return temp1;
}
return 0;
}

2.8判断链表是否有环路,获取连接点,计算环的长度

此题很有意思,具体详细请参考:http://www.cnblogs.com/xudong-bupt/p/3667729.html

判断是否含有环:slow和fast,slow指针每次走一步,fast指针每次走两步,若是链表有环,fast必能追上slow(相撞),若fast走到NULL,则不含有环。

//判断是否含有环
bool containLoop(Node* head){
if (head==NULL) {
return false;
}
Node* slow = head;
Node* fast = head;
while (slow!=fast&&fast->next!=NULL) {
slow = slow->next;
fast = fast->next->next;
}
if (fast==NULL) {
return false;
}
return true;
}

判断环的长度:在相撞点处,slow和fast继续走,当再次相撞时,slow走了length步,fast走了2*length步,length即为环得长度。

//获得环的长度
int getLoopLength(Node* head){
if (head==NULL) {
return 0;
}
Node* slow = head;
Node* fast = head;
while (slow!=fast&&fast->next!=NULL) {
slow = slow->next;
fast = fast->next->next;
}
if (fast==NULL) {
return 0;
}
//slow和fast首次相遇后,slow和fast继续走
//再次相遇时,即slow走了一圈,fast走了两圈
int length = 0;
while (slow!=fast) {
length++;
slow = slow->next;
fast = fast->next->next;
}
return length;
}

环得连接点:slow和fast第一次碰撞点到环的连接点的距离=头指针到环的连接点的距离,此式可以证明,详见上面链接。

//获得环的连接点
Node* getJoinpoit(Node* head){
if (head==NULL) {
return NULL;
}
Node* slow = head;
Node* fast = head;
while (slow!=fast&&fast->next!=NULL) {
slow = slow->next;
fast = fast->next->next;
}
if (fast==NULL) {
return NULL;
}
Node* fromhead = head;
Node* fromcrashpoint = slow; while (fromcrashpoint!=fromhead) {
fromhead = fromhead->next;
fromcrashpoint = fromcrashpoint->next;
}
return fromhead;
}

2.9二叉树和双向链表转化

二叉树和双向链表转化指的是,二叉树节点结构和双向链表的结构想类似,只不过二叉树节点的节点存储的两个指针为左右子数,而双向链表存储的是前后节点。题意为将二叉树的某种遍历转化为链表存储。此题很明显该用递归,读者可以画图体会一下指针变化。

二叉树节点或双向链表节点定义:

struct BinaryTreeNode{
int value;
BinaryTreeNode* left;
BinaryTreeNode* right;
};

二叉树的中序遍历转换为双向链表

BinaryTreeNode* convertNode(BinaryTreeNode* pNode, BinaryTreeNode** pLastNodeInLast){
if (pNode == NULL) {
return NULL;
}
BinaryTreeNode *pCurrent = pNode;
if (pCurrent->left != NULL) {
convertNode(pCurrent->left, pLastNodeInLast);
} pCurrent->left = *pLastNodeInLast;
if (*pLastNodeInLast != NULL) {
(*pLastNodeInLast)->right=pCurrent;
} *pLastNodeInLast = pCurrent;
if (pCurrent->right != NULL) {
convertNode(pCurrent->right, pLastNodeInLast);
}
return NULL;
} BinaryTreeNode* convertBTToDLL(BinaryTreeNode* root){
BinaryTreeNode *pLastNodeInLast = NULL;
convertNode(root, &pLastNodeInLast);
BinaryTreeNode *pHeadOfList = pLastNodeInLast;
while (pHeadOfList != NULL && pHeadOfList->left != NULL) {
pHeadOfList = pHeadOfList->left;
}
return pHeadOfList;
}

数据结构之链表-链表实现及常用操作(C++篇)的更多相关文章

  1. [数据结构]P1.1 链表结构

    * 注: 本文/本系列谢绝转载,如有转载,本人有权利追究相应责任. 2019年4月8日 Stan Zhang 2019年4月8日  格物致知,经世致用. [面试题]1.为什么要用链表? 数组具有的缺陷 ...

  2. SDUT-2121_数据结构实验之链表六:有序链表的建立

    数据结构实验之链表六:有序链表的建立 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入N个无序的整数,建立一个有序链 ...

  3. SDUT-2120_数据结构实验之链表五:单链表的拆分

    数据结构实验之链表五:单链表的拆分 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 输入N个整数顺序建立一个单链表,将该 ...

  4. SDUT-2119_数据结构实验之链表四:有序链表的归并

    数据结构实验之链表四:有序链表的归并 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 分别输入两个有序的整数序列(分别包 ...

  5. SDUT-3331_数据结构实验之链表八:Farey序列

    数据结构实验之链表八:Farey序列 Time Limit: 10 ms Memory Limit: 600 KiB Problem Description Farey序列是一个这样的序列:其第一级序 ...

  6. SDUT-2122_数据结构实验之链表七:单链表中重复元素的删除

    数据结构实验之链表七:单链表中重复元素的删除 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 按照数据输入的相反顺序(逆 ...

  7. javascript数据结构与算法--链表

    链表与数组的区别?  1. 定义: 数组又叫做顺序表,顺序表是在内存中开辟一段连续的空间来存储数据,数组可以处理一组数据类型相同的数据,但不允许动态定义数组的大小,即在使用数组之前必须确定数组的大小. ...

  8. python数据结构与算法——链表

    具体的数据结构可以参考下面的这两篇博客: python 数据结构之单链表的实现: http://www.cnblogs.com/yupeng/p/3413763.html python 数据结构之双向 ...

  9. Python数据结构之单链表

    Python数据结构之单链表 单链表有后继结点,无前继结点. 以下实现: 创建单链表 打印单链表 获取单链表的长度 判断单链表是否为空 在单链表后插入数据 获取单链表指定位置的数据 获取单链表指定元素 ...

随机推荐

  1. Freemarker页面静态化技术,activemq监听页面变动

    初步理解: 架构优化: 静态页面的访问速度优于从缓存获取数据的动态页面的访问速度: Freemarker: 导包 模板:hello.ftl <!DOCTYPE html> <html ...

  2. 串String(1):串的实现(定长顺序存储结构)

    前言 PS:本文相关头文件.预编译以及typedef如下,阅读一遍以便于下面的理解: #include <stdio.h> #include <stdlib.h> #inclu ...

  3. 命令行执行php脚本 中$argv和$argc

    在实际工作中有可能会碰到需要在nginx命令行执行php脚本的时候,当然你可以去配置一个conf用外网访问. 在nginx命令行中 使用 php index.php 就可以执行这个index.php脚 ...

  4. Mac下nvm管理node.js版本问题

    本篇文章主要是针对已经安装了node.js和nvm管理工具小伙伴遇到的问题. 管理工具有两个,一个是nvm,还有一个是nnvm的好处就是可以管理多个node版本,而且可以切换想要的版本,可以安装一个稳 ...

  5. c#的关键字

    abstract as base bool break byte case catch char checked decimal default delegate continue double do ...

  6. input 上传图片显示预览、调用摄像头,ios和Android的兼容性解决

    html代码: <img id="pic" src="img/pic.png"/> </span><input id=" ...

  7. python匿名函数

    文章导读: 以前自己一直没搞明白Python中的匿名函数,现在拿这个问题基本上搞明白了,拿自己的理解整成一篇文章,附带大量例子,让其更加好理解. 在编程语言中,函数的应用: 1. 代码块重复,这时候必 ...

  8. 无法打开文件“freeglut.lib”解决方法:

    资源: 链接:https://pan.baidu.com/s/1eSctT5K 密码:174s VS2010问题: 无法打开文件"freeglut.lib"解决方法: (1)下载f ...

  9. 纯CSS实现箭头、气泡让提示功能具有三角形图标(简单实例)

    <style type="text/css"> /*向上箭头,类似A,只有三个边,不能指定上边框*/ .arrow-up { width: 0px; height: 0 ...

  10. Lucene.net(4.8.0) 学习问题记录二: 分词器Analyzer中的TokenStream和AttributeSource

    前言:目前自己在做使用Lucene.net和PanGu分词实现全文检索的工作,不过自己是把别人做好的项目进行迁移.因为项目整体要迁移到ASP.NET Core 2.0版本,而Lucene使用的版本是3 ...