B+树的插入删除和 c/c++ 代码实践

  B+树是从B树修改而来的。对 B 树的建立会了的话,对 B+ 树的操作可以类比着进行。
  关于 B 树,可以参看之前写的文章:对B树的插入删除理解和c/c++代码实践
https://blog.csdn.net/zhangzhangkeji/article/details/119767646
  B+ 树的节点的结构体定义如下:

struct Node {
	int numKeys;
	int keyArray[MAX];
	Node* ptChildren[MAX] = {NULL};  //?????????指针需要初始化么?
	Node* ptParent;
	Node* ptRightBrother;
};	

  m 阶 B+ 树的特点是:
  (1)树里节点最多有 m 个 指针 ,指向 m 个子节点 。
  (2)分支节点最少可以有 ceil(m/2) 个指针 。如 m = 9, 则最少有5 个指针。m = 10 也最少有 5 个指针 。根节点若也是叶节点,最少可以没有;若根节点是分支节点,最少有2个指针。
  (3)节点里关键字个数 和指针个数是相等的。关键字的值是其对应指针指向的子树的最大关键字值。所以,节点里有几个元素,就有几个子树。关键字就是元素。
  (4)如 m = 9 则非根节点最少含有 5 个关键字, m = 10 ,则非根节点最少含有 5 个关键字。当一个节点太小,与相邻节点合并时,刚好可以两个最小节点合并成一个最大节点或仅比最大节点少一个元素。一个最大节点刚好也可以拆分成两个最小节点。这也是最小指针数与最大阶数成 二分之一 关系的意义。
  (5)B+ 树 始终有两个头指针,一个指向根节点,另一个指向含有最小元素的叶节点,即最左边的叶节点。所以,既可以从根节点开始对关键字进行跳跃式查找。也可以对所有叶节点里的关键字进行顺序查找。
  (6)B+ 树里 关键字是重复的。 所有分支节点里的关键字都会在叶节点里再出现一次。所有叶节点含有了不重复的树里所有关键字。分支节点里的关键字,更像是索引,指示了其所有子树的关键字的取值范围。这也为跳跃式查找提供了选择依据。
  (7)B+ 树的插入,也是插入到合适的叶节点里,若叶节点太大,则拆分成两个叶节点,并在其父母节点里插入一个元素和一个指向新的节点的指针。如果父母节点太大,则拆分父母节点,依次类推,直到根节点被拆分,生成新的根节点,树的高度加一。若插入的是叶节点里最大的元素,则要修改其祖先节点里元素的值,分支节点里元素值代表对应子树的最大元素值。
  (8)B+ 树的所有叶节点,也要求在同一条水平线上,即在树里同样的深度上。
  (9)B+ 树的删除,只能对叶节点里的元素进行直接删除。若删除的元素是叶节点的最大元素,因为其也出现在分支节点,需要把分支节点里的该值也删除,修改为叶节点新的最大值。若删除后,叶节点太小,则可以从其左右兄弟叶节点借一个元素。若其左右兄弟节点都是最小节点,则合并该节点和其右兄弟节点(这只是规定,合并其和左兄弟节点也可以,但同一个程序,只能选择一个方向,保持一致性和程序计算的简单化),并从其父母节点里删除一个元素,一个指针。若父母节点也太小了,则合并父母节点和父母节点的右兄弟节点。若合并后的父母节点太大,则还需要拆分父母节点。使父母节点有合适的爷爷奶奶节点。若根节点太大被拆分,则生成新的根。分支节点里出现的元素被删除时,因为其代表子树的最大元素,所以要用子树里的第二最大元素替代之。
  (10)合并节点的操作不一定非得是两个最小节点。对于分支节点的合并,必须考虑合并后的分支节点太大的问题。
  (11)程序建立 B+ 树正确的话,对叶节点里关键字的遍历,依据根节点从上往下从左往右遍历到叶节点,和直接对叶节点的顺序遍历,其结果是相同的。
  (12)因为要求可以对所有叶节点从左到右顺序遍历,所有节点类型里还要有一个指针指向其右侧兄弟节点,当然对于叶节点,这个指针才有意义。只需要对叶节点建立右指针的连接。
  (13)B+ 树已经不适合再用中序遍历了,因为对于典型的二叉树中序遍历(左根右),B+ 树的指针数目和关键字数目是相同的。而且B+ 树可以直接对叶节点进行顺序遍历。中序递归遍历的存在对于 B+ 树已经不重要了。
  (14)复杂的程序,在编写之前,最好可以列出流程图,明确程序执行步骤,逐步细化,也降低了大脑的想象力负担。
  接着给出B+ 树插入操作的流程图:
在这里插入图片描述
然后是B+ 树删除操作的流程图:
在这里插入图片描述

  各函数功能如下:
  函数searchNode : 从节点中查找元素应该存在的位置。
  函数searchTree : 从整棵树中查找某个元素的位置。
  函数newRootVerti:生成新的根节点。
  函数split : 拆分过大的节点。
  函数insertNode : 往节点里插入元素和指针。
  函数insertTree : 往树里添加元素。
  函数dispalyCommaExp:显示一棵树的逗号表达式。以检验程序是否正确。
  函数sequenTraverseLeafNodes:顺序遍历叶节点。
  函数deleteKeyFromNode : 从节点里删除元素和指针。
  函数smallNode:处理出现的过小节点。
  函数deleteKeyFromTree:从树里删除元素。

  以下是完整代码。先是main函数所在源文件代码:

#include<iostream>
using namespace std;
#define MAX 8
#define ORDER 5
#define MAXKEYS ORDER 
#define MINKEYS (ORDER + 1) / 2

struct Node {
	int numKeys;
	int keyArray[MAX];
	Node* ptChildren[MAX] = {NULL};  //?????????指针需要初始化么?
	Node* ptParent;
	Node* ptRightBrother;
};	


struct Result {
	Node* pt;
	int index;
	bool findOrNot;
};

extern int searchNode(Node*& pt, int key);
extern Result searchTree(Node * & rootVerti,int key);

extern void newRootVerti(Node*& rootVerti, int keyLeft, Node* ptLeft, int keyRight ,Node* ptRight);
extern void split(Node*& rootVerti, Node* ptLeft);
extern void insertNode(Node * &rootVerti,Node* pt, int index, int key, Node* ptKey);
extern void insertTree(Node*& rootVerti, int key,Node * & rootHoriz);

extern void dispalyCommaExp(Node * & rootVerti);
extern void sequenTraverseLeafNodes(Node*& rootHoriz);

extern void deleteKeyFromNode(Node*& rootVerti, Node* pt, int index, Node* ptIndex);


extern void smallNode(Node * & rootVerti,Node* pt);
extern bool deleteKeyFromTree(Node*& rootVerti,int key,Node * & rootHoriz);
extern void combine(Node*& rootVerti, Node* ptParent, int indexLeft, int indexRight, Node* ptLeft, Node* ptRight);


int main() {
	Node* rootVerti = NULL, * rootHoriz = NULL;
//	Result result;
//	int array[] = { 4, 9, 0, 1, 8, 6, 3, 5, 2, 7,10},length = 11;
//	int array[] = { 1, 8, 5, 2, 7,10}, length = 6;
//	int array[] = { 4},length = 1 ;
//	int array[] = { 4,0,9,6},length = 4 ;
	int array[] = { 1,2,6,7,11,4,8,13,10,5,17,9,16,20,3,12,14,18,19,15,21,22,23,24,25,26,27,28,29,30},length = 30;
//	int array[] = { 1,2,6,7,11,4,8,13,10,5,17,9,16,20,3,12,14,18,19,15 }, length = 20;


	for (int i = 0; i < length ; i++) 
		insertTree(rootVerti, array[i],rootHoriz);

	dispalyCommaExp(rootVerti);
	cout << endl;
	sequenTraverseLeafNodes(rootHoriz);
	cout << endl;

	cout << "input number after 'next' to continue test,but 100 means stop test.eg.next:100";
	cout << endl << "input the number you want to delete : ";
	int input;
	do{
		cin >>input ;
		
		cout << endl;
		if (input == 100)
			break;

		if(deleteKeyFromTree(rootVerti,input,rootHoriz) == false)
			cout << "list does not have this key : "<<input<<'.';
		else {
			cout << "逗号表达式:";
			dispalyCommaExp(rootVerti);
			cout << endl;
			cout << "叶节点遍历:";
			sequenTraverseLeafNodes(rootHoriz);
		}
		cout<< "  next :";
	} while (1 == 1);

	return 0;
}

接着是各函数所在源文件代码:

#include<iostream>
using namespace std;
#define MAX 8
#define ORDER 5
#define MAXKEYS ORDER 
#define MINKEYS (ORDER + 1) / 2

struct Node {
	int numKeys;
	int keyArray[MAX];
	Node* ptChildren[MAX] = {NULL};
	Node* ptParent;
	Node* ptRightBrother;
};

struct Result {
	Node* pt;
	int index;
	bool findOrNot;
};

int searchNode(Node*& pt, int key) {
	int index;
	for (index = 0; index < pt->numKeys && pt->keyArray[index] < key; index++);
	return index; 
}
Result searchTree(Node*& rootVerti, int key) {   //返回的是其应该在叶节点中的插入位置
	Result result;				//或者其在树中第一次出现的位置
	Node* pt = rootVerti, * ptParent = NULL;
	int index = 0;

	while (pt != NULL) {
		index = searchNode(pt, key);

		if (index == pt->numKeys) {
			while (pt->ptChildren[0] != NULL)
				pt = pt->ptChildren[pt->numKeys - 1];

			result.findOrNot = false;
			result.pt = pt;
			result.index = pt->numKeys;
			return result;
		}
		else if (pt->keyArray[index] != key) {
			ptParent = pt;
			pt = pt->ptChildren[index];
		}
		else
			break;
	}

	if (pt == NULL) {
		result.findOrNot = false;
		result.pt = ptParent;
	}
	else {
		result.findOrNot = true;
		result.pt = pt;
	}
	result.index = index;

	return result;
}

void newRootVerti(Node*& rootVerti, int keyLeft, Node* ptLeft, int keyRight, Node* ptRight) {
	rootVerti = new Node;

	rootVerti->ptParent = NULL;

	rootVerti->numKeys = 2;
	rootVerti->keyArray[0] = keyLeft;
	rootVerti->keyArray[1] = keyRight;

	rootVerti->ptChildren[0] = ptLeft;
	ptLeft->ptParent = rootVerti;

	rootVerti->ptChildren[1] = ptRight;
	ptRight->ptParent = rootVerti;

	if (ptLeft->ptChildren[0] == NULL) {  //防止根节点以下的就是叶节点,为其建立横向连接
		ptRight->ptRightBrother = ptLeft->ptRightBrother;
		ptLeft->ptRightBrother = ptRight;
	}
}

void split(Node*& rootVerti, Node* ptLeft) {
	void insertNode(Node * &rootVerti, Node * pt, int index, int key, Node * ptKey);
	Node* ptRight = new Node;

	ptRight->numKeys = MINKEYS;
	ptLeft->numKeys = ptLeft->numKeys - MINKEYS;

	ptRight->ptParent = ptLeft->ptParent;

	for (int i = 0, j = ptLeft->numKeys; i < MINKEYS; i++) {
		ptRight->keyArray[i] = ptLeft->keyArray[j + i];
		ptRight->ptChildren[i] = ptLeft->ptChildren[j + i];

		if (ptRight->ptChildren[i] != NULL)
			ptRight->ptChildren[i]->ptParent = ptRight;
	}

	if (ptLeft == rootVerti)  //根节点被拆分,生成新根
		newRootVerti(rootVerti,ptLeft->keyArray[ptLeft->numKeys - 1],ptLeft,
			ptRight->keyArray[ptRight->numKeys - 1],ptRight);
	else {
		int i = searchNode(ptLeft->ptParent,ptLeft->keyArray[ptLeft->numKeys - 1]);
		ptLeft->ptParent->keyArray[i] = ptLeft->keyArray[ptLeft->numKeys - 1];

		insertNode(rootVerti,ptRight->ptParent,i + 1,
			ptRight->keyArray[ptRight->numKeys - 1],ptRight);
	}
}

void insertNode(Node*& rootVerti, Node* pt, int index, int key, Node* ptKey) {
	int i;
	for (i = pt->numKeys; i > index; i--) {
		pt->keyArray[i] = pt->keyArray[i - 1];
		pt->ptChildren[i] = pt->ptChildren[i - 1];
	}

	pt->keyArray[index] = key;
	pt->numKeys++;

	pt->ptChildren[index] = ptKey;

	if (ptKey != NULL && ptKey->ptChildren[0] == NULL) {  //说明插入的是叶节点
		pt->ptChildren[index]->ptRightBrother = pt->ptChildren[index - 1]->ptRightBrother;
		pt->ptChildren[index - 1]->ptRightBrother = pt->ptChildren[index];
	}

	if (pt->ptChildren[0] == NULL && 
		key == pt->keyArray[pt->numKeys - 1]) {//插入的是叶节点最大元素
		Node* ptTemp = pt->ptParent;			//也许也是整棵树最大元素
		int index,keyOld = pt->keyArray[pt->numKeys - 2];
		while (ptTemp != NULL) {
			index = searchNode(ptTemp, keyOld);
			ptTemp->keyArray[index] = key;

			if (index < ptTemp->numKeys - 1)
				break;
			ptTemp = ptTemp->ptParent;
		}
	}
	
	if (pt->numKeys > ORDER)
		split(rootVerti, pt);
}
void insertTree(Node*& rootVerti, int key, Node*& rootHoriz) {
	Result result = searchTree(rootVerti,key);

	if (result.findOrNot == false)
		if (rootVerti == NULL) {
			rootHoriz = rootVerti = new Node;

			rootHoriz->ptRightBrother = NULL;
			rootVerti->ptParent = NULL;

			rootHoriz->numKeys = 1;
			rootVerti->keyArray[0] = key;
		}
		else
			insertNode(rootVerti,result.pt,result.index,key,NULL);
}

void dispalyCommaExp(Node*& rootVerti) {
	if (rootVerti == NULL)
		return;

	int i;
	cout << '[';
	for (i = 0; i < rootVerti->numKeys - 1; i++)
		cout << rootVerti->keyArray[i] << ' ';
	cout << rootVerti->keyArray[i] << ']';

	if (rootVerti->ptChildren[0] != NULL) {
		cout << '(';
		for (i = 0; i < rootVerti->numKeys - 1; i++) {
			dispalyCommaExp(rootVerti->ptChildren[i]);
			cout << ',';
		}
		dispalyCommaExp(rootVerti->ptChildren[i]);
		cout << ')';
	}
}

void sequenTraverseLeafNodes(Node*& rootHoriz) {
	Node* pt = rootHoriz;
	while (pt != NULL) {
		for (int i = 0; i < pt->numKeys; i++)
			cout << pt->keyArray[i] << ' ';
		pt = pt->ptRightBrother;
	}
}


void deleteKeyFromNode(Node*& rootVerti, Node* pt, int index,Node * ptIndex) {
	void smallNode(Node * &rootVerti, Node * pt);
	int max = pt->keyArray[index];

	for (int j = index; j < pt->numKeys - 1; j++) {
		pt->keyArray[j] = pt->keyArray[j + 1];
		pt->ptChildren[j] = pt->ptChildren[j + 1];
	}
	pt->numKeys--;

	if(ptIndex == NULL)				//若被删元素是原叶节点最大元素
		if (index == pt->numKeys) {   //需要修改其祖先节点存储的最大元素值
			Node* ptTemp = pt->ptParent;
			while (ptTemp != NULL) {
				index = searchNode(ptTemp, max);
				ptTemp->keyArray[index] = pt->keyArray[pt->numKeys - 1];
				
				if (index < ptTemp->numKeys - 1)
					break;
				ptTemp = ptTemp->ptParent;
			}
		}

	if (ptIndex != NULL && ptIndex->ptChildren[0] == NULL) //叶节点被删
		pt->ptChildren[index - 1]->ptRightBrother = ptIndex->ptRightBrother;
	
	delete ptIndex;

	if (pt->ptParent != NULL && pt->numKeys < MINKEYS)
		smallNode(rootVerti,pt);
}

void combine(Node*& rootVerti, Node* ptParent, int indexLeft,int indexRight,Node* ptLeft, Node* ptRight) {
	for (int i = 0; i < ptRight->numKeys; i++) {
		ptLeft->keyArray[ptLeft->numKeys + i] = ptRight->keyArray[i];
		ptLeft->ptChildren[ptLeft->numKeys + i] = ptRight->ptChildren[i];
	
		if (ptLeft->ptChildren[0] != NULL)
			ptRight->ptChildren[i]->ptParent = ptLeft;
	}

	ptLeft->numKeys = ptLeft->numKeys + ptRight->numKeys;

	ptParent->keyArray[indexLeft] = ptRight->keyArray[ptRight->numKeys - 1];

	if (ptParent == rootVerti && rootVerti->numKeys == 2) {
		if (ptLeft->ptChildren[0] == NULL) //如果根节点以下就是叶节点
			ptLeft->ptRightBrother = ptRight->ptRightBrother;

		delete rootVerti;
		delete ptRight;
		rootVerti = ptLeft;
		rootVerti->ptParent = NULL;
	}
	else
		deleteKeyFromNode(rootVerti,ptParent,indexRight,ptRight);

	if (ptLeft->numKeys > MAXKEYS)
		split(rootVerti,ptLeft);
}

void smallNode(Node*& rootVerti, Node* pt) {
	Node* ptParent = pt->ptParent, * ptLeft, * ptRight;
	int index = searchNode(ptParent,pt->keyArray[pt->numKeys - 1]);

	if (index == 0) {
		ptRight = ptParent->ptChildren[index + 1];
		if (pt->ptChildren[0] == NULL && ptRight->numKeys > MINKEYS) {
			insertNode(rootVerti, pt, pt->numKeys, ptRight->keyArray[0], NULL);
			deleteKeyFromNode(rootVerti, ptRight, 0, NULL);
		}
		else
			combine(rootVerti, ptParent, 0, 1, pt, ptRight);
	}
	else if (index == ptParent->numKeys - 1) {
		ptLeft = ptParent->ptChildren[index - 1];
		if (pt->ptChildren[0] == NULL && ptParent->ptChildren[index - 1]->numKeys > MINKEYS) {
			insertNode(rootVerti, pt, 0, ptLeft->keyArray[ptLeft->numKeys - 1], NULL);
			deleteKeyFromNode(rootVerti, ptLeft, ptLeft->numKeys - 1, NULL);
		}
		else
			combine(rootVerti,ptParent,index - 1,index,ptLeft,pt);
	}
	else {
		ptLeft = ptParent->ptChildren[index - 1];
		ptRight = ptParent->ptChildren[index + 1];
		if (pt->ptChildren[0] == NULL) 
			if (ptLeft->numKeys > MINKEYS) {
				insertNode(rootVerti, pt, 0, ptLeft->keyArray[ptLeft->numKeys - 1], NULL);
				deleteKeyFromNode(rootVerti, ptLeft, ptLeft->numKeys - 1, NULL);
				return;
			}
			else if (ptRight->numKeys > MINKEYS) {
				insertNode(rootVerti, pt, pt->numKeys, ptRight->keyArray[0], NULL);
				deleteKeyFromNode(rootVerti, ptRight, 0, NULL);
				return;
			}

		combine(rootVerti,ptParent,index,index + 1,pt,ptRight);
	}
}

bool deleteKeyFromTree(Node*& rootVerti, int key, Node*& rootHoriz) {
	Result result = searchTree(rootVerti,key);
	
	if (result.findOrNot == false)
		return false;

	if (result.pt->ptChildren[0] != NULL) {
		Node* ptTemp = result.pt->ptChildren[result.index];
		while (ptTemp->ptChildren[0] != NULL)
			ptTemp = ptTemp->ptChildren[ptTemp->numKeys - 1];

		deleteKeyFromNode(rootVerti, ptTemp, ptTemp->numKeys - 1,NULL);
	}
	else if (result.pt == rootVerti && rootVerti->numKeys == 1) {
		delete rootVerti;
		rootVerti = rootHoriz = NULL;
	}
	else
		deleteKeyFromNode(rootVerti,result.pt,result.index,NULL);

	return true;
}

接着是测试结果,30个数字的大 B+ 树。一一从树中删除到空树。足够的测试才可以检验程序全面的性能。
在这里插入图片描述

在这里插入图片描述
程序变量命名见名知意,易于理解和跟踪程序。
谢谢阅读。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C++ 实现的一个 B 树类,有注释,但不知道我写得能不能看明白。使用时先要从类 CDataTypeForBtree 派生一个类出来,在派生类中实现数据比较函数 Compare,数据输出函数 Print。然后在堆上创建一个数据类对象,把地址作为构造函数的参数创建树对象,下面给出一个使用示例。 #include <string.h> #include <stdio.h> #include "DataTypeForBtree.h" #include "BTree.h" struct tree_data { char tid[37]; char tname[63]; unsigned int aid; unsigned int id; }; class CMyDataTypeForBtree : public CDataTypeForBtree { public: virtual void Print(void *key, FILE* fp) { if(NULL == key || NULL == fp) return; struct tree_data* tr = (struct tree_data*)key; fprintf(fp, "%u %u %s %s\n", tr->id, tr->aid, tr->tid, tr->tname); } virtual int Compare(void *p1, void *p2) { if(NULL == p1 || NULL == p2) return (int)(((unsigned int)(~0)) >> 1); //返回一个大点的数表示失败 struct tree_data *tr1, *tr2; tr1 = (struct tree_data*)p1; tr2 = (struct tree_data*)p2; if(tr2->aid != tr1->aid) { return tr1->aid - tr2->aid; } if('\0' == tr1->tid[0] && '\0' == tr2->tid[0]) { return strcmp(tr1->tname, tr2->tname); } return strcmp(tr1->tid, tr2->tid); } CMyDataTypeForBtree() { } virtual ~CMyDataTypeForBtree() { } }; int main(int argc, char* argv[]) { CMyDataTypeForBtree *dt = new CMyDataTypeForBtree; CBTree tree(dt, 5); struct tree_data tr[101] = {{"asd", "4Hero", 1, 1}, {"abc", "Underworld", 1, 0}, {"bac", "Samantha", 1, 2}, {"cass", "Gelka", 1, 3}, {"mark", "Clark", 1, 4}, {"gone", "Woolfy", 1, 5}, {"word", "Production", 1, 6}, {"paper", "Jimpster", 1, 7}, {"Richie", "Hawtin", 1, 8}, {"John", "Matthias", 1, 9}, {"Lou", "Donaldson", 1, 10}, {"Lady", "Alma", 1, 11}, {"Mass", "Slick", 1, 12}, {"Clyde", "Alexander", 1, 13}, //……省略若干,省略部分在下载包里面有 {"", "I'M Not Sayin' Get 'Er Done, But Don'T Just Stand There", 11, 101},}; for(int i = 0; i < 101; i++) { tree.Insert((void*)(tr + i)); } tree.DelKey((void*)(tr + 5)); tree.Traverse(NULL, NULL); tree.DelKey((void*)(tr + 13)); tree.Traverse(NULL, NULL); //输出到标准输出,可以重定向到文件 return 0; }
C++ Primer中文版(第5版)[203M]分3个压缩包 本书是久负盛名的C++经典教程,其内容是C++大师Stanley B. Lippman丰富的实践经验和C++标准委员会原负责人Josée Lajoie对C++标准深入理解的完美结合,已经帮助全球无数程序员学会了C++。 对C++基本概念和技术全面而且权威的阐述,对现代C++编程风格的强调,使本书成为C++初学者的最佳指南;对于中高级程序员,本书也是不可或缺的参考书。 目录 第1章 开始 1   1.1 编写一个简单的C++程序 2   1.1.1 编译、运行程序 3   1.2 初识输入输出 5   1.3 注释简介 8   1.4 控制流 10   1.4.1 while语句 10   1.4.2 for语句 11   1.4.3 读取数量不定的输入数据 13   1.4.4 if语句 15   1.5 类简介 17   1.5.1 Sales_item类 17   1.5.2 初识成员函数 20   1.6 书店程序 21   小结 23   术语表 23   第Ⅰ部分 C++基础 27   第2章 变量和基本类型 29   2.1 基本内置类型 30   2.1.1 算术类型 30   2.1.2 类型转换 32   2.1.3 字面值常量 35   2.2 变量 38   2.2.1 变量定义 38   2.2.2 变量声明和定义的关系 41   2.2.3 标识符 42   2.2.4 名字的作用域 43   2.3 复合类型 45   2.3.1 引用 45   2.3.2 指针 47   2.3.3 理解复合类型的声明 51   2.4 const限定符 53   2.4.1 const的引用 54   2.4.2 指针和const 56   2.4.3 顶层const 57   2.4.4 constexpr和常量表达式 58   2.5 处理类型 60   2.5.1 类型别名 60   2.5.2 auto类型说明符 61   2.5.3 decltype类型指示符 62   2.6 自定义数据结构 64   2.6.1 定义Sales_data类型 64   2.6.2 使用Sales_data类 66   2.6.3 编写自己的头文件 67   小结 69   术语表 69   第3章 字符串、向量和数组 73   3.1 命名空间的using声明 74   3.2 标准库类型string 75   3.2.1 定义和初始化string对象 76   3.2.2 string对象上的操作 77   3.2.3 处理string对象中的字符 81   3.3 标准库类型vector 86   3.3.1 定义和初始化vector对象 87   3.3.2 向vector对象中添加元素 90   3.3.3 其他vector操作 91   3.4 迭代器介绍 95   3.4.1 使用迭代器 95   3.4.2 迭代器运算 99   3.5 数组 101   3.5.1 定义和初始化内置数组 101   3.5.2 访问数组元素 103   3.5.3 指针和数组 105   3.5.4 C风格字符串 109   3.5.5 与旧代码的接口 111   3.6 多维数组 112   小结 117   术语表 117   第4章 表达式 119   4.1 基础 120   4.1.1 基本概念 120   4.1.2 优先级与结合律 121   4.1.3 求值顺序 123   4.2 算术运算符 124   4.3 逻辑和关系运算符 126   4.4 赋值运算符 129   4.5 递增和递减运算符 131   4.6 成员访问运算符 133   4.7 条件运算符 134   4.8 位运算符 135   4.9 sizeof运算符 139   4.10 逗号运算符 140   4.11 类型转换 141   4.11.1 算术转换 142   4.11.2 其他隐式类型转换 143   4.11.3 显式转换 144   4.12 运算符优先级表 147   小结 149   术语表 149   第5章 语句 153   5.1 简单语句 154   5.2 语句作用域 155   5.3 条件语句 156   5.3.1 if语句 156   5.3.2 switch语句 159   5.4 迭代语句 165   5.4.1 while语句 165   5.4.2 传统的for语句 166   5.4.3 范围for语句 168   5.4.4 do while语句 169   5.5 跳转语句 170   5.5.1 break

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值