数组
- 相同类型的数据的集合{ },通过索引访问元素;
- 在内存中连续存储,属于顺序表;
- 插入、删除时间复杂度 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为结束字符
- {‘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 基础篇
下一篇:函数