数据结构之链表-链表实现及常用操作(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. python文件操作及os模块常用命令

    1.文件打开 文件句柄 = open('文件路径', '模式') 2.文件操作 打开文件时,需要指定文件路径和以何等方式打开文件,打开后,即可获取该文件句柄,日后通过此文件句柄对该文件操作. 三种基本 ...

  2. [译文]React v16(新特性)

    [译文]React v16(新特性) 查看原文内容 我们很高兴的宣布React v16.0发布了! 这个版本有很多长期被使用者期待的功能,包括: fragments (返回片段类型) error bo ...

  3. 一些常用的vim编辑器快捷键:

    一些常用的vim编辑器快捷键: h」.「j」.「k」.「l」,分别控制光标左.下.上.右移一格. 按「ctrl」+「b」:屏幕往“后”移动一页. 按「ctrl」+「f」:屏幕往“前”移动一页. 按「c ...

  4. S7-200和S7-300profibus-DP通信

    一.S7-200CN的cup可以通过EM277接入DP网络 二.CPU315-2DP做主站,S7-200CUP做从站 三. 通信题目 四.硬件组态 1.主站的DP组态,地址为2 2.EM277作为从站 ...

  5. 506. Relative Ranks

    Given scores of N athletes, find their relative ranks and the people with the top three highest scor ...

  6. HNOI2013 BZOJ3142 数列

    尝试用Markdown写一篇博客 3142: [Hnoi2013]数列 Description 小T最近在学着买股票,他得到内部消息:F公司的股票将会疯涨.股票每天的价格已知是正整数,并且由于客观上的 ...

  7. 查看内存和cpu

    top: 主要参数 d:指定更新的间隔,以秒计算. q:没有任何延迟的更新.如果使用者有超级用户,则top命令将会以最高的优先序执行. c:显示进程完整的路径与名称. S:累积模式,会将己完成或消失的 ...

  8. mysql case when group by实例

    mysql 中类似php switch case 的语句. select xx字段, case 字段 when 条件1 then 值1 when 条件2 then 值2 else 其他值 END 别名 ...

  9. 我的第一个python web开发框架(19)——产品发布相关事项

    好不容易小白将系统开发完成,对于发布到服务器端并没有什么经验,于是在下班后又找到老菜. 小白:老大,不好意思又要麻烦你了,项目已经弄完,但要发布上线我还一头雾水,有空帮我讲解一下吗? 老菜:嗯,系统上 ...

  10. centos 7 部署 汉化版 gitlab

    =============================================== 2017/11/12_第6次修改                       ccb_warlock 更 ...