C++ 教程 - 02 复合数据类型

2023-12-13 09:38:57

数组

  • 相同类型的数据的集合{ },通过索引访问元素;
  • 在内存中连续存储,属于顺序表;
  • 插入、删除时间复杂度 O ( n ) O(n) O(n),访问复杂度 O ( 1 ) O(1) O(1)
  • 定义数组,首先确定内部的数据类型,并指定长度(常量),初始化时只能使用{ }来初始化;
// 定义
int arr[30] = {1, 2, 3}; // 后面的值默认为0,初始值个数不能超过长度

// 不指定长度时,需要初始化
double score[] = {79.5, 80.5, 90}; 

// 访问数组,注意索引越界问题
for(int i=0; i < sizeof(arr) / sizeof(arr[0]) ; i++){
	cout << "arr[" << i << "]:" << arr[i] << endl;
}

// 仅仅获取元素
for(int e:arr){
	cout << e << endl;
}

?

  • 数组内部嵌套数组,即二维数组
// 定义二维数组
int arr[2][3] = {
		{1,2,3},
		{4,5,5}
	};

// 遍历
int rowCount = sizeof(arr) / sizeof(arr[0]);
int colCount = sizeof(arr[0]) / sizeof(arr[0][0]);
for(int i=0; i<rowCount; i++){
	for(int j=0; j<colCount; j++){
		cout << arr[i][j] << endl;
	}
}

// 元素遍历
for(auto& row:arr){ // 复合数据类型需要 &引用
	for(auto e:row){ // auto 自动类型
		cout <<e << endl;	
	}
}

?

  • 数组实现 选择排序

int main() {
	
	int arr[] = {4, 3, 2, 1, 5};
	// 选择排序O(n^2)
	// 每次选择最大值或者最小值,放在最后一位
	// n个数 做n-1次选择
	int arrLen = sizeof(arr) / sizeof(arr[0]);
	int n = arrLen - 1;
	int temp;
	while (n >= 1) { // 控制选择次数
		int max_idx = 0; // 
		for (int i = 1; i <= n; i++) {
			if (arr[i] > arr[max_idx]) {
				max_idx = i;
			}
		}
		// 交换
		if (max_idx != n) {
			// 交换
			temp = arr[n];
			arr[n] = arr[max_idx];
			arr[max_idx] = temp;
		}
		n--;
	}

	for (int i = 0; i < arrLen; i++) {
		cout << arr[i] << endl;
	}
  • 数组实现 冒泡排序

int main() {
	
	int arr[] = {4, 3, 2, 1, 5};
	// 冒泡排序 O(n^2)
	// 每次将较大值向后一位冒泡
	// n个数 做n-1轮冒泡
	int arrLen = sizeof(arr) / sizeof(arr[0]);
	int temp;
	for (int i = 0; i < arrLen - 1; i++) { // 控制冒泡的轮数
		
		// 控制冒泡
		for (int j = 0; j < arrLen - 1 - i; j++) {
		
			if (arr[j] > arr[j + 1]) {
				temp = arr[j + 1];
				arr[j + 1] = arr[j];
				arr[j] = temp;
			}
		}
	}

	for (int i = 0; i < arrLen; i++) {
		cout << arr[i] << endl;
	}

	// system("dir");
	system("pause");
	return 0;
}

?

vector

  • 数组的长度固定,且越界操作可能带来未知的风险,故对数组扩展,得到vector,array;
  • vector容器,也是相同类型的数据的集合,长度可变,但性能降低;
  • array容器,也是相同类型的数据的集合,长度固定,但性能降低;
#include <vector>
#include <array>
using namespace std;  // 必须包含头文件,并使用std 


int main() {
	// 容器是对数组的扩展,不定长用vector, 定长用array (也是扩展的数组)
	// 拷贝初始化
	vector<int> cont1 = {1, 2, 3}; 
	vector<float> cont2{ 34.5, 46.7 }; //  赋值号可省略

	// 直接初始化
	vector<double> cont3(5); // 指定长度为5  可以继续添加数据,扩展长度
	vector<string> cont4(3, "jack"); // 长度为3,且默认值均为"jack"

	// 容器  通过索引访问,修改
	for (int i = 0; i < cont1.size(); i++) {
		cout << cont1[i] << "\t";
	}
	// 添加
	cont1.push_back(100); // 尾部添加
	//cont1.pop_back(); // 尾部删除
	for (int e : cont1) {
		cout << e << " ";
	}

	// 基于数组扩展的array , 固定长度
	array<string, 5> userList = { "jack", "tom", "lucy" }; // 赋值也可省略
	// array<string, 5> userList;  
	// userList = { "jack", "tom", "lucy" };   // 先声明,再赋值 userList.size() 长度
	for (string e : userList) {
		cout << e << " ";
	}

	return 0;
}

字符串

  • string类型,也是一个容器,内容可变,通过索引访问每个字符;
  • size()方法获取长度,size() - 1是最后一个索引;同数组,也不能越界访问/赋值
  • 支持两种for循环
#include <string>
using namespace std;


int main() {
	// c++ 中的string是可变的容器,而python的str是不可变的容器
	string name; //初始化为空
	string sex = "male"; //拷贝初始化
	string addr("wuhan province"); // 直接初始化

	// 普通的for
	for (int i = 0; i < addr.size(); i++) {
	
		cout << addr[i] << " "; // 索引访问每个字符
	}

	// 元素遍历的for
	for (char e : addr) {
		cout << e << " ";
	}

	return 0;
}
  • 字符串的拼接
    • 字符串变量 + 字符串变量;
    • 字符串变量 + 字符串字面量;
    • 字符串字面量 + 字符串字面量 (X); 不同于python
  • 字符串的比较
    • ==、> >= < <= 逐字符比较;
  • c风格的字符串,字符类型的数组;
    • {‘a’,‘b’, ‘c’, ‘\0’} , \0为结束字符
      ?

输入输出

  • cin 控制台输入,捕获字符串,遇空格则将空格前的整体作为一个字符串,赋值给变量;
string name;
string last_name;
cout << "input your name:"; // 控制台输出
cin >> name ; // 输入"jack li", 只能捕获到 "jack"
cin >> name >> last_name;   // 捕获到"li" 赋值给 last_name  
  • getline 获取一行
string name;
getline(cin, name); //  获取一行,并赋值给name
  • cin.get() 获取一个字符
char sex;
sex = cin.get();  // or cin.get(sex)
  • fstream 文件操作
    • 包含头文件<fstream>;
    • ifstream readFile(“xxx.txt”) 读取文件;
    • ofstream toFile(“target.txt”) 写入到文件;
    • 案例,输入用户的姓名,年龄,学历,性别,电话,保存到 user.txt 文件中,并读取该文件,逐行打印;
#include <iostream>
#include <string>  // 字符串的 头文件
#include <fstream> // 文件操作的头文件
using namespace std;


// 枚举 性别
enum Sex {
	Male,  // 默认从0开始, 也可以赋值指定从2、4等开始
	Female
};


int main() {
	// 姓名  学历   手机号
	string name, stuLevel, phone;
	int age = 18;
	string ageLabel = to_string(age); // 整型转为字符串
	// 性别
	string sex_label;
	enum Sex sex = Male;

	// 用户输入
	cout << "input your name:";
	getline(cin, name); // 获取一行  回车则结束本次输入
	cout << "input your stuLevel:";
	cin >> stuLevel;
	cout << "input your phone:";
	cin >> phone;
	cout << "input your age and sex separated with whitespace:";
	cin >> age >> sex_label; // 捕获多个空格分割的字符串
	// 枚举 重新赋值
	if (sex_label == "0")
		sex = Male;  // 对应int数值
	else
		sex = Female;

	
	// 字符串拼接
	string totalContent = "姓名:";
	totalContent += name + "\n" + "年龄:";
	totalContent += ageLabel + "\n" + "性别:";  // 必须字符串对象开头  开始拼接
	totalContent += sex_label + "\n" + "学历:";
	totalContent += stuLevel + "\n" + "手机号:";
	totalContent += phone;
	

	system("cd C:\\Users\\lenovo\\Desktop");
	// 写入文件
	ofstream writeToFile("C:\\Users\\lenovo\\Desktop\\user.txt");
	// << 流输出   到文件对象中
	writeToFile << totalContent;
	// 关闭文件对象
	writeToFile.close();


	// 读取文件
	ifstream readFileObj("C:\\Users\\lenovo\\Desktop\\user.txt");

	//逐单词 读取
	/*string word;
	while (readFileObj >> word) {
		cout << word << endl;
	}*/

	// 逐行读取
	string oneLine;
	while (getline(readFileObj, oneLine)) {
		cout << oneLine << endl;
	}

	//逐字符读取
	/*char c;
	while (readFileObj.get(c)) {
		cout << c << endl;
	}*/


	return 0;
}

?

结构体

  • 结构体是一个复合的数据类型,可以存储不同的数据类型;
    • 内部可以定义基本类型;
    • 也可以定义结构体类型;
    • 通过. 访问内部属性,并可修改;
    • 定义学生结构体,包含学生的基本信息;
      • 姓名
      • 年龄
      • 班级
      • 同学s
#include <iostream>
#include <string>  // 字符串的 头文件
#include <fstream> // 文件操作的头文件
#include <vector>  // 变长容器
using namespace std;


// 班级结构体
struct StuClass {   // 使用struct关键字 定义结构体
	string name;   // 班级的名字
	int stuNum;
	string classMaster;
}; // 此行 }后面还可以声明变量,只是不常用


// 学生结构体
struct StuInfo {
	string name;
	int age;
	StuClass myClass;
	vector<StuInfo> myClassmates; // 同学
};


//输出一个学生的信息
void printStu(StuInfo stu) {
	cout << "姓名:" << stu.name << endl;
	cout << "年龄:" << stu.age << endl;
	cout << "班级:" << stu.myClass.name << endl;
	cout << "同学数:" << stu.myClassmates.size() << endl;
}


int main() {	

	// 结构体类   c++ 可以直接使用,c中需要带着struct一起使用
	// 创建班级
	StuClass class1 = { "三年级01", 30, "teacher wang" }; // 赋值可以省略
	//创建学生
	StuInfo stu1 = { "jack", 23, class1, {} };
	StuInfo stu2 = { "tom", 18, class1, {} };

	// 为stu2 添加一个同学
	stu2.myClassmates.push_back(stu1);

	// 输出学生信息
	printStu(stu1);
	printStu(stu2);
	return 0;
}

?

枚举

  • 把可以取的值,都一 一地列举出来;
  • 使用关键字enum定义枚举;
// 如之前使用的性别的枚举
enum Sex{
	Male,  // 默认从0开始, 可以在任意项处指定起始值
	Female, // 1
}

//定义变量
Sex sex = Male; // 以枚举项 赋值
Sex sex1 = Sex(2);  // 以整数的强制类型转换赋值,不能直接复制int

// 枚举 周工作日
enum WEEK {
	Mon,
	Tue,
	Wed,
	Thu = 10, // 从10开始
	Fri,
	Sat,
	Sun
};

?

指针

  • 变量即存储数据的地址空间的名字;
  • 指针即存储数据的地址空间的地址,即指针变量 存放的是地址
  • 地址占用8bytes;
    在这里插入图片描述

int main() {	

	int a = 10;
	int* p; // 指针变量
	p = &a;  // 指针变量   赋值a的地址

	cout << "指针:" << p << endl;
	cout << "指针的值:" << *p << endl;
	
	// 指针操作改值
	*p = 20;
	cout << "修改后的值:" << *p << endl;
	
	return 0;
}
  • 未初始化的指针,即无效指针(野指针),不可直接使用;
  • 空指针,指针赋值nullptr / NULL / 0; 声明指针,必须初始化
  • void* 指针,可以赋值任意类型的地址,但不能解引用;
  • int** p ,指向指针的指针;解引用使用** ;
  • const int* p, 指向常量的指针,只能赋值常量的地址;
  • int* const p, 常量指针, 不可改变量p的地址;

int main() {	

	// 常量
	const int a = 4, b = 5;
	
	// 指向常量的指针
	const int* p = &a;
	cout << *p << endl;

	p = &b;
	cout << *p << endl;

	// 指向变量的  常量指针
	int c = 10;
	int* const p2 = &c;  // 存储地址不可修改
	cout << *p2 << endl;
	
	return 0;
}
  • 数组名称为指向内部首元素的地址,即首地址;
  • 指针数组,指针组成的数组,int* arr[5];
  • 数组指针,代表数组的指针;

int main() {	

	int a = 1, b = 2, c = 3;

	// 指针组成的数组-->指针数组
	int* arr[5];
	arr[0] = &a;
	arr[1] = &b;
	arr[3] = &c;
	
	cout << *(arr[0]) << endl;
	cout << *(arr[1]) << endl;

	// 数组指针-- 指向一个数组的指针
	int arr2[3];
	int* p = arr2;  // 或者  int(*p)[3]
	*p = 1;
	*(p + 1) = 2; // 地址偏移一个数据类型的长度
	*(p + 2) = 3;
	for (int e : arr2) {
		cout << e << endl;

	}
	// 数组指针
	int(*pp)[3]; // *pp 相当于arr2
	pp = &arr2; // 取地址
	cout << arr2 << endl; // 数组的首地址
	cout << pp << endl; // 数组的首地址
	cout << *pp << endl; // *pp 为arr2   还是首地址
	cout << **pp << endl; // 解引用首个元素
	cout << *(*pp + 1) << endl; // 解引用第二个元素
	return 0;
}

?

引用

  • 引用,是给一个变量起别名,一旦绑定一个变量,再不可绑定其他变量;
  • 定义引用, 如 int&
int a = 10;
// 定义引用(必须引用变量)
int& af = a;  // 必须 声明同时 初始化,指向同一个内存地址
af = 20;
cout << a << endl; // 20

int& aaf = af;  // 引用 的引用  使用同引用;
  • 常量引用
const int aa = 20;

// 常量的引用
const int& aaref = aa;  // 引用常量
const int& bbref = 10;  // 引用字面量

int a = 10;
const int& ccref = a;  // 引用变量
  • 引用相当于指针常量;
    ?

综合案例

  • 反转一个数组
    • 思路:两边对称的元素互换;

int main() {	

	// 定义数组容器
	array<int, 5> arr = { 4,3,2,1,5 }; // 两边对换
	int temp;
	int n = arr.size();
	for (int i = 0; i < n / 2; i++) {
		temp = arr[i];
		arr[i] = arr[n - 1 - i];
		arr[n - 1 - i] = temp;
	}

	for (int e : arr) {
		cout << e << " ";
	}
	cout << endl;
	return 0;
}
  • 两个字符串相加
    • 从个位逐位,没有的一方用0代替;
    • label表示进位,有进位则为1(进位最多为1),无进位则为0;
    • 从字符串的最后一个索引开始向索引0方向,循环处理;
    • 字符串通过索引访问每一个字符,数值字符减去‘0’字符得到其代表的整数值;
    • int转为字符串使用 to_string(int);
    • 字符串的拼接必须字符串对象在左

int main() {	

	// 字符串相加
	string s1 = "39999"; // 可变容器,索引访问,获取一个字符   '2' - '0'  得到对应的数值2
	string s2 = "2";  // 字符串-->int   stoi("5793")  stof stod
	string result = "";
	int temp;

	int s1Idx = s1.size() - 1;
	int s2Idx = s2.size() - 1;
	
	// 表示是否有进位 为1有进位 为0无进位
	int mid = 0;
	int a, b;
	while (s1Idx >= 0 || s2Idx >= 0 || mid) { // 逐位相加,没有则用0代替,特殊情况:两者长度相同,且最后有进位

		a = s1Idx >= 0 ? s1[s1Idx] - '0' : 0; // 当前字符  减去 '0'字符 得到自己的整数值
		b = s2Idx >= 0 ? s2[s2Idx] - '0' : 0;
		// 求和
		temp = a + b + mid;
		
		if (temp <= 9) {// 本次求和没有进位
			result = to_string(temp) + result; // int 转为 string
			mid = 0;
		}
		else {
			// 有进位
			mid = temp / 10;
			result = to_string(temp % 10) + result;
		}
		
		s1Idx--;
		s2Idx--;
	}
	

	cout << "计算结果:" << result << endl;
	return 0;
}
  • 反转一个单向链表,应用的知识点如下。
    • 包含自定义的头文件使用#include “xxx.h”
    • 空指针初始化 NULL/nullptr,值为0;
    • 指针的解引用+调用属性等价于 指针 -> 调用属性;

定义结构体

// 自定义一个头文件lauf.h , 并定义结构体
struct LinkNode {
	int value;
	LinkNode* next;
};

主程序

#include <iostream>
#include "lauf.h" // 包含自定义的头文件
using namespace std;


// 入口函数
int main() {

	// 创建单链表  7->8->9->NULL    空指针为0
	LinkNode* myLink;
	LinkNode node9 = { 9, nullptr }; // nullptr 空指针 值为0
	LinkNode node8 = { 8, &node9 };
	LinkNode node7 = { 7, &node8 };
	myLink = &node7;

	// 双指针 方式反转
	LinkNode* curPtr = myLink;
	LinkNode* prePtr = nullptr;
	LinkNode* temp;
	while (curPtr) { // 指针存在 
		temp = (*curPtr).next;  //  解引用 再调用属性  等价于  curPtr -> next
		curPtr->next = prePtr;
		
		// 移动指针
		prePtr = curPtr;
		curPtr = temp;
	}

	LinkNode* reversedLink = prePtr;   // 指针变量赋值 
	// 打印反转后的链表
	while (reversedLink) {
		cout << reversedLink->value << "->";  // -> 等价于  指针的解引用+调用属性
		reversedLink = reversedLink->next;
	}

	return 0;
}

?
?
上一篇:C++ 教程 - 01 基础篇
下一篇:函数

文章来源:https://blog.csdn.net/weixin_45228198/article/details/134891554
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。