数据结构和算法(05)---链表(c++)

文章目录

  • 目录
    • 链表的基本概念
      • 1.数组和链表
    • 链表的使用
      • 1.链表的简单使用
      • 2.链表的进阶使用
      • 3.链表的高阶使用
      • 4.链表的其他操作
    • 链表容器list
      • 1.list介绍
      • 2. list使用
      • 3. list与vector之间的区别
      • 4.list例子代码

目录

  • 数据结构:
    • 逻辑结构:数组,栈,队列,字符串,树,图
    • 存储结构:顺序存储,链式存储
  • C++常用的数据结构有:string , stack , queue , deque , vector , list , map , iterators.

链表的基本概念

链表是一种线性表,其在内存中以链式结构存储,所以叫做链表。具有插入和删除方便,但是访问则必须从链表的头开始。

1.数组和链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

链表的使用

1.链表的简单使用

一个个节点按顺序串接起来,就构成了链表。显然这一个个节点是很关键的,假设我们要构造一个int类型的链表,那么一个节点中就需要包含两个元素:

  • 一个是当前节点所保存的值,设为int value。
  • 另一个就是指向下一个节点的指针,我们再假设这个节点类是node,那么这个指针就是 node *next。

这里一定不是int *next。因为这个指针指向的下一个元素是一个类的实例,而不是int类型的数据。那么node这个类最简单的实现就如下:

class node  
{  
public:  int value;  node *next;  node()  {  value = 0;  next = NULL;  }  
}; 

这个类名字为node,包含两个元素,一个是当前node的值,一个是指向下一个节点的指针,还有一个构造函数,分别将value初始化为0、next初始化为NULL
拿到这个类以后,假设我们生成两个这个类的实例,node1和node2,再将node1的next指针指向node2,就构成了有两个元素的链表。这样如果我们拿到node1这个节点,就可以通过next指针访问node2。比如下面的代码

#include <iostream>
#include <deque>using namespace std;class node{
public:int value;//链表节点的数据域node* next;//用于指向下一个节点,是链表中的指针域node():value(0),next(NULL){};//默认构造函数node(int value):value(value),next(NULL){};//带参数的构造函数 ~ node(){cout<<"delete the node!!!"<<endl;delete next; // 删除节点的指针域 } 
}; int main(){node mNode1(1),mNode2(2),mNode3(3),mNode4(4);mNode1.next = &mNode2;mNode2.next = &mNode3;mNode3.next = &mNode4;cout<<"the value of node3 is :"<<mNode3.value<<endl;  //直接输出节点 cout<<"the value of node3 is :"<<mNode1.next->next->value<<endl;  //通过链表查找 return 0;
} 

the value of node3 is :3
the value of node3 is :3
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
在这里插入图片描述

2.链表的进阶使用

上述这样就构成了一个最简单的链表,如果还有新的节点出现,那么就如法炮制,链在表尾或者表头,当然插在中间也是没问题的。

但是这样还有个问题就是node1和node2是我们提前声明好的,而且知道这两个实例的名称,如果我们需要1000甚至跟多节点,这种方式显然是不科学的,而且在很多时候,我们都是动态生成一个类的实例,返回的是这个实例的首地址。

下面的代码我们用一个for循环,生成11个节点,串起来形成一个链表
原理就是先生成一个头结点,然后动态生成10个节点,每生成一个节点,就将这个节点指向头结点,然后更新头结点为当前节点

#include <iostream>
#include <deque>using namespace std;class Node{
public:int value;//链表节点的数据域Node* next;//用于指向下一个节点,是链表中的指针域Node():value(0),next(NULL){};//默认构造函数Node(int value):value(value),next(NULL){};//带参数的构造函数 ~ Node(){cout<<"delete the node!!!"<<endl;delete next; // 删除节点的指针域 } 
}; int main(){Node *head , *curr;  //定义头结点指针和当前插入节点的指针head = new Node();//头插法生成链表 for(int i = 0 ; i < 10 ; i++ ){curr = new Node(i);if(curr==NULL){cerr<<"内存分配失败!"<<endl; }else{curr->next = head->next;  //将当前节点指针指向之前头结点指向的地方head->next = curr;  //头结点指向当前的节点cout<<"the value is : "<<curr->value<<endl;}}return 0;
} 

the value is : 0
the value is : 1
the value is : 2
the value is : 3
the value is : 4
the value is : 5
the value is : 6
the value is : 7
the value is : 8
the value is : 9

那么链表该如何遍历呢,刚开头的时候就说,遍历链表需要从头到尾,访问每一个元素,直到链表尾。也就是说不断地访问当前节点的next,直到NULL。下面是链表的遍历输出

#include <iostream>using namespace std;class Node{
public:int value;//链表节点的数据域Node* next;//用于指向下一个节点,是链表中的指针域Node():value(0),next(NULL){};//默认构造函数Node(int value):value(value),next(NULL){};//带参数的构造函数 ~ Node(){cout<<"delete the node!!!"<<endl;delete next; // 删除节点的指针域 } 
}; int main(){Node *head , *curr;head = new Node();//头插法生成链表 for(int i = 0 ; i < 10 ; i++ ){curr = new Node(i);if(curr==NULL){cerr<<"内存分配失败!"<<endl; }else{curr->next = head->next;head->next = curr;
//			cout<<"the value is : "<<curr->value<<endl;}}//链表的遍历while(head){cout<<"current data is : "<<head->value<<endl;head = head->next;} return 0;
} 

current data is : 0
current data is : 9
current data is : 8
current data is : 7
current data is : 6
current data is : 5
current data is : 4
current data is : 3
current data is : 2
current data is : 1
current data is : 0

3.链表的高阶使用

链表相对于数组有个非常明显的优点就是能以时间复杂度o(1)完成一个节点的插入或者删除操作。

插入操作的原理很简单,假设现在有三个节点,一个是当前节点curr,一个是当前节点的下一个节点,也就是后继节点,假设为next,还有一个待插入的节点,假设为insert。插入操作就是让当前节点的后继节点指向insert节点,insert节点的后继节点指向next节点。以下是示意图
在这里插入图片描述
删除操作的原理也是类似的,就是让当前节点的后继节点指向它后继节点的后继节点。示意图如下

在这里插入图片描述

那么插入和删除操作用代码如何实现呢,我们还用原先的链表,先插入一个值为20的节点,输出链表的全部元素。然后再删除链表中这个值为20的元素,输出元素的全部内容。代码如下:

#include <iostream>
#include <deque>using namespace std;class Node{
public:int value;//链表节点的数据域Node* next;//用于指向下一个节点,是链表中的指针域Node():value(0),next(NULL){};//默认构造函数Node(int value):value(value),next(NULL){};//带参数的构造函数 ~ Node(){cout<<"delete the node!!!"<<endl;delete next; // 删除节点的指针域 } 
}; int main(){Node *head , *curr;head = new Node();//头插法生成链表 for(int i = 0 ; i < 10 ; i++ ){curr = new Node(i);if(curr==NULL){cerr<<"内存分配失败!"<<endl; }else{curr->next = head->next;head->next = curr;}}//链表的插入curr = head;while(curr->value != 5){   //第一步:找到要插入的节点的前驱 curr = curr->next;}cout<<"curret node is : "<<curr->value<<endl;Node* insertNode = new Node(55);  //创建要插入的节点 insertNode->next = curr->next;  //第二步:将插入的节点的指针域指向当前节点的后继 curr->next = insertNode;  //第三步:将当前节点的指针指向插入的新节点//链表遍历curr = head;while(curr){cout<<"the value is : "<<curr->value<<endl;curr = curr->next;}//链表元素的删除//第一步找到要删除的节点的前继curr = head;while(curr->next->value != 55){curr = curr->next;}cout<<"curret node is : "<<curr->value<<endl;Node *tempNode;tempNode = curr->next;  //第二步:保存下要删除的节点 curr->next = tempNode->next; //第三步:将要删除节点的前继指针指向要删除节点的后继 delete tempNode;  //第四步:删除当前的节点//链表遍历curr = head;while(curr){cout<<"the value is : "<<curr->value<<endl;curr = curr->next;}return 0;
} 

curret node is : 5
the value is : 0
the value is : 9
the value is : 8
the value is : 7
the value is : 6
the value is : 5
the value is : 55
the value is : 4
the value is : 3
the value is : 2
the value is : 1
the value is : 0
curret node is : 5
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
delete the node!!!
the value is : 0
the value is : 9
the value is : 8
the value is : 7
the value is : 6
the value is : 5
the value is : 4
the value is : 3
the value is : 2
the value is : 1
the value is : 0
至于完整的链表,STL中有标准的库,也有功能非常全面的API,只要我们知道内部的实现原理,调用这些API是非常简单的事,用起来也会得心应手。

4.链表的其他操作

// 在末尾加入新的结点
void AddToTail(ListNode** pHead, int value){ListNode* pNew = new ListNode();pNew->val = value;pNew->next = nullptr;if (*pHead == nullptr){*pHead = pNew;}else{ListNode* pNode = *pHead;while(pNode->next != nullptr)pNode = pNode->next;pNode->next = pNew;}return;
}// 删除某个值为value的结点
void RemoveNode(ListNode** pHead, int value){if(pHead == nullptr || *pHead == nullptr) return;ListNode* pToDeleted = nullptr;if((*pHead)->val == value){pToDeleted = *pHead;*pHead = (*pHead)->next;}else{ListNode* pNode = *pHead;while(pNode->next != nullptr && pNode->next->val != value)pNode = pNode->next;if(pNode->next != nullptr && pNode->next->val == value){pToDeleted = pNode->next;pNode->next = pNode->next->next;}}if(pToDeleted != nullptr){delete pToDeleted;pToDeleted = nullptr;}return;
}

链表容器list

1.list介绍

在这里插入图片描述

2. list使用

在这里插入图片描述
在这里插入图片描述

3. list与vector之间的区别

在这里插入图片描述

4.list例子代码

#include <iostream>
#include <list>using namespace std;int main(){list<int> c1;list<int>::iterator c1_iter;//向链表的末尾加入元素 c1.push_back(1);c1.push_back(2);c1.push_back(3);//使用迭代器访问链表 for(c1_iter = c1.begin();c1_iter != c1.end();c1_iter++){cout<<"data is : "<<*c1_iter<<endl;}//将链表反转 c1.reverse();for(c1_iter = c1.begin();c1_iter != c1.end();c1_iter++){cout<<"reverse data is : "<<*c1_iter<<endl;}return 0;
}

data is : 1
data is : 2
data is : 3
reverse data is : 3
reverse data is : 2
reverse data is : 1

#include <list>  
#include <iostream>  int main( )  
{  using namespace std;  list <int> c1;  list <int>::iterator c1_Iter;  c1.push_back( 20 );  c1.push_back( 10 );  c1.push_back( 30 );  cout << "Before sorting: c1 =";  for ( c1_Iter = c1.begin( ); c1_Iter != c1.end( ); c1_Iter++ )  cout << " " << *c1_Iter;  cout << endl;  c1.sort( );  cout << "After sorting c1 =";  for ( c1_Iter = c1.begin( ); c1_Iter != c1.end( ); c1_Iter++ )  cout << " " << *c1_Iter;  cout << endl;  c1.sort( greater<int>( ) );  cout << "After sorting with 'greater than' operation, c1 =";  for ( c1_Iter = c1.begin( ); c1_Iter != c1.end( ); c1_Iter++ )  cout << " " << *c1_Iter;  cout << endl;  
}

Before sorting: c1 = 20 10 30
After sorting c1 = 10 20 30
After sorting with ‘greater than’ operation, c1 = 30 20 10

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445442.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文阅读 状态压缩

状态压缩 Abstract 信息学发展势头迅猛&#xff0c;信息学奥赛的题目来源遍及各行各业&#xff0c;经常有一些在实际应用中很有价值的问题被引入信息学并得到有效解决。然而有一些问题却被认为很可能不存在有效的(多项式级的)算法&#xff0c;本文以对几个例题的剖析&#xf…

数据结构和算法(06)---二叉树(c++)

文章目录目录二叉树1.二叉树的基本概念2.二叉树的应用和时间复杂度3.二叉树的插入4.二叉树的查找5. 二叉树的遍历6.二叉树的删除二叉树的基本操作1.二叉树的基础操作2.代码实现创建二叉树和三种遍历二叉树的方法目录 数据结构&#xff1a; 逻辑结构&#xff1a;数组&#xff0c…

如何转载CSDN博客

前言 对于喜欢逛CSDN的人来说&#xff0c;看别人的博客确实能够对自己有不小的提高&#xff0c;有时候看到特别好的博客想转载下载&#xff0c;但是不能一个字一个字的敲了&#xff0c;这时候我们就想快速转载别人的博客&#xff0c;把别人的博客移到自己的空间里面&#xff0c…

CSDN写博客(字体颜色、大小)

markdown里面的标记语言可以使用标签对来实现对文本文字颜色大小信息的控制。下面给出几个实例&#xff1a; 黑体字示例 微软雅黑示例 华文彩云示例 color#00ffff size可以根据实际大小进行设置&#xff0c;一般不超过7。 红色字体CSDN 红色字体CSDN 使用十六进制颜色值 …

bose qc30 安静的城市是什么样子

使用感受 网友1&#xff08;20岁&#xff09;&#xff1a; 当你带着这个耳机听音乐的时候&#xff0c;有一种感觉&#xff0c;感觉这个世界都是你歌曲里的MV&#xff0c;这个枯燥乏味的世界都被赋予了你心中的那份情感&#xff0c;这种感觉&#xff0c;真的很棒 网友2&#…

DeepLearning.ai 提炼笔记(5-1)-- 循环神经网络

参考博客 Class 5: 序列模型Sequence Models Week 1: 循环神经网络RNN (Recurrent) 文章目录Class 5: 序列模型Sequence ModelsWeek 1: 循环神经网络RNN (Recurrent)目录序列模型-循环神经网络1.序列模型的应用2.数学符号3.循环神经网络模型传统标准的神经网络循环神经网络的…

常见人工智能比赛平台总结

目录1.kaggle比赛1.1 kaggle比赛是什么&#xff1f;1.2 为什么举办kaggle比赛&#xff1f;1.3 kaggle比赛形式是什么&#xff1f;1.4 kaggle比赛的奖励制度是什么&#xff1f;2.阿里天池比赛2.1 阿里天池比赛是什么&#xff1f;2.2 为什么举办阿里天池比赛&#xff1f;2.3 阿里…

机器学习模型评分总结(sklearn)

文章目录目录模型评估评价指标1.分类评价指标acc、recall、F1、混淆矩阵、分类综合报告1.准确率方式一&#xff1a;accuracy_score方式二&#xff1a;metrics2.召回率3.F1分数4.混淆矩阵5.分类报告6.kappa scoreROC1.ROC计算2.ROC曲线3.具体实例2.回归评价指标3.聚类评价指标1.…

kaggle (02) - 房价预测案例(进阶版)

房价预测案例&#xff08;进阶版&#xff09; 这是进阶版的notebook。主要是为了比较几种模型框架。所以前面的特征工程部分内容&#xff0c;我也并没有做任何改动&#xff0c;重点都在后面的模型建造section Step 1: 检视源数据集 import numpy as np import pandas as pd读…

《Head First设计模式》第二章笔记 观察者模式

背景 客户有一个WeatherData对象&#xff0c;负责追踪温度、湿度和气压等数据。现在客户给我们提了个需求&#xff0c;让我们利用WeatherData对象取得数据&#xff0c;并更新三个布告板&#xff1a;目前状况、气象统计和天气预报。 WeatherData对象提供了4个接口&#xff1a; …

《Head First设计模式》第三章笔记 装饰者模式

装饰者模式&#xff08;Decorator Pattern) *利用组合&#xff08;composition&#xff09;和委托&#xff08;delegation&#xff09;可以在运行时实现继承行为的效果&#xff0c;动态地给对象加上新的行为。 *利用继承扩展子类的行为&#xff0c;是在编译时静态决定的&#x…

机器学习中如何解决数据不平衡问题?

文章目录目录什么是数据不平衡问题&#xff1f;数据不平衡会造成什么影响&#xff1f;如何处理数据不平衡问题&#xff1f;1、重新采样训练集1.1随机欠抽样1.2.基于聚类的过采样2.使用K-fold交叉验证3.转化为一分类问题4.组合不同的重采样数据集5.用不同比例重新采样6.多模型Ba…

《Head First设计模式》第四章笔记 工厂模式

之前我们一直在使用new操作符&#xff0c;但是实例化这种行为并不应该总是公开的进行&#xff0c;而且初始化经常会造成耦合问题&#xff0c;工厂模式将摆脱这种复杂的依赖&#xff0c;本次内容包括简单工厂&#xff0c;工厂方法和抽象工厂三种情况。 1 2 3 4 5 6 Duck duck&a…

《Head First设计模式》第五章笔记-单件模式

单件模式 定义&#xff1a;确保一个类只有一个实例&#xff0c;并提供全局访问点。 编写格式&#xff1a; 1 2 3 4 5 6 public class MyClass{ private MyClass(){}//构造方法私有化 public static MyClass getInstance(){ //提供全局访问点 return new My…

《Head First设计模式》第六章笔记-命令模式

封装调用-命令模式 命令模式可将“动作的请求者”从“动作的执行者”对象中解耦。 本篇中将不再描述书中所引入的“巴斯特家电自动化公司”的遥控器控制案例&#xff0c;而使用简单易懂的餐厅案例。 在开始之前&#xff0c;让我们通过一个现实中的例子来了解命令模式。 理解…

一文读懂机器学习库graphLab

文章目录目录什么是graphlab为什么使用graphlab?如何安装graphlab?graphlab的简单使用。目录 什么是graphlab GraphLab 是由CMU&#xff08;卡内基梅隆大学&#xff09;的Select 实验室在2010 年提出的一个基于图像处理模型的开源图计算框架&#xff0c;框架使用C语言开发实…

《Head First设计模式》第七章-适配器模式、外观模式

适配器模式 适配器模式是什么&#xff0c;你一定不难理解&#xff0c;因为现实中到处都是。比如说&#xff1a; 如果你需要在欧洲国家使用美国制造的笔记本电脑&#xff0c;你可能需要使用一个交流电的适配器…… 当你不想改变现有的代码&#xff0c;解决接口不适配问题&#…

《Head First设计模式》第八章笔记-模板方法模式

模板方法模式 之前所学习的模式都是围绕着封装进行&#xff0c;如对象创建、方法调用、复杂接口的封装等&#xff0c;这次的模板方法模式将深入封装算法块&#xff0c;好让子类可以在任何时候都将自己挂接进运算里。 模板方法定义&#xff1a;模板方法模式在一个方法中定义一…

机器学习基础-吴恩达-coursera-(第一周学习笔记)----Introduction and Linear Regression

课程网址&#xff1a;https://www.coursera.org/learn/machine-learning Week 1 —— Introduction and Linear Regression 目录 Week 1 Introduction and Linear Regression目录一 介绍1-1 机器学习概念及应用1-2 机器学习分类 二 单变量的线性回归2-1 假设函数hypothesis2…

常见8种机器学习算法总结

简介 机器学习算法太多了&#xff0c;分类、回归、聚类、推荐、图像识别领域等等&#xff0c;要想找到一个合适算法真的不容易&#xff0c;所以在实际应用中&#xff0c;我们一般都是采用启发式学习方式来实验。通常最开始我们都会选择大家普遍认同的算法&#xff0c;诸如SVM&a…