数据结构学习记录(实时更新)

1.递归时间复杂度和空间复杂度的计算
2.链表实现C++
定义链表结构的是一个自引用数据结构,里面的指针是指向自己

struct ListNode
{
	int  value;
	ListNode *next;
};

3.C++中NULL和0和nullptr区别
看大佬的描述,在C++里 NULL就被看作0,跟0的作用一样,但是写作NULL的话可能因为你不把它看作0导致某些时候有重载函数的时候发生错误,所以不如写成0,再加上C++里 0就是空指针,而nullptr是C++11里专门表示空指针的,所以以后还是多用nullptr比较好,避免歧义。
4.C++链表的构造 遍历
花了两个多小时才搞懂链表的创建和遍历过程。。希望以后别忘了
首先链表是一个动态内存的数据结构,要新建一个空的节点要先用new申请内存空间,这个new就是申请一个内存空间,返回一个指向这个空间的指针,也不能给它命名,我开始就在想不就是个结构体吗,干脆每个节点都给它定义一个对象不就完了,为什么一定要用指针来访问它,现在终于明白了。。。这才方便动态管理内存吧
实现过程是看大佬的实现代码
贴一下大佬的博客
https://blog.csdn.net/qq_41620518/article/details/81143414

#include <iostream>
using namespace std;
/* 创建一个单链表 */
struct ListNode{
    int m_key;
    ListNode* next;
};
void createList(ListNode* pHead){
    ListNode* p = pHead;
    for (int i = 1; i < 10; ++i) {
        ListNode* pNewNode = new ListNode;
        pNewNode->m_key = i; // 将新节点的值赋值为i
        pNewNode->next = NULL;
        p->next = pNewNode; // 上一个节点指向这个新建立的节点
        p = pNewNode; // p节点指向这个新的节点
    }
}
int main(){
    ListNode* head = NULL;
    head = new ListNode;
    head->m_key = 0;
    head->next = NULL;
    createList(head);
 
    return 0;
}

这个还有一点就是,既然知道了头节点的指针,为什么还要新建一个新的指针,另它等于头指针,大佬的解释是头指针是new出来的,地址不再变化,所以这个新的指针就可以自己移动,不影响头指针对后面的节点进行处理了,这也解释了为什么比如头指针是head 那么head = head->next 会删掉第一个节点,而新建的指针 p=p->next只是移动它指向的位置,而不改变原来的链表了。
知道了这个遍历过程就更简单了,定义一个新的指针等于头指针,然后顺着遍历输出即可。
5.C++实现单链表反转Reverse
核心在循环部分,先保存头节点指向的下个节点的地址,然后让头节点指向新链表的头,然后分别移动指向新旧两链表的指针指向对应的新头指针,循环,直到旧链表为空,然后更新输入链表,返回即可。

ListNode* Reverse(ListNode* head){
	ListNode *oldhead = head;
	ListNode *newhead = nullptr;
	ListNode *tem;
	while(oldhead){
		tem = oldhead->next;
		oldhead->next = newhead;
		newhead = oldhead;
		oldhead = tem;}
	head = newhead;
	return head;
}

6.汉诺塔问题实现(递归思想)这个看答案很简单。。但是自己想还是不会写啊。。怎么设置参数什么的。。。

#include <iostream>
#include <cstdio>
using namespace std;

void move(int n,int start,int end,int temp)
{
	if(n>=1){
	move(n-1,start,temp,end);
	printf("move %d form %d to %d.\n",n,start,end);
	move(n-1,temp,end,start);}
}
int main()
{
	move(3,1,3,2);
	return 0;
}

7.C++数组实现线性表,大佬给出了完整的代码,就是构造一个线性表类,里面包含了各种线性表的操作集,每一个都是一个成员函数。
线性表结构的实现就是使用了私有成员来表示的,包括了一个数组,和一个int变量,分别保存线性表里的数据和线性表的长度。
我在自己写的时候发现自己的两个问题,一个是模板函数从来没用过。。重新看了一下才会用了,还有就是成员函数里的操作会直接影响数据成员的值,我理解也就是每个成员函数都隐含了引用输入了数据成员作为参数了。

具体实现看大佬的就好
https://blog.csdn.net/qq_30611601/article/details/79516986

8.C++链表实现线性表,这个稍微有点复杂,因为它的结构使用链表表示的,所以要先有链表的结构,看上面就有,是用结构体写出来的,包含一个data存数据,一个指针指向下一个。然后还有就是它的默认构造函数要写一个链表头,有参数的构造函数就直接读取一个数组来表示,其次就是析构函数,因为链表用new申请的空间,所以析构函数注意要delete申请的空间,不然会导致内存泄漏,构造函数和析构函数注意一下,剩下的就是实现各种操作集,把链表搞懂,实现起来简单轻松加愉快。我自己对着大佬了写了一部分,主要是构造函数,析构函数,遍历等操作,还有一点就是。。。我发现这个东西只看书是没用的,不自己写一些永远不知道自己哪里不会。

#include <iostream>
using namespace std;

template <class DataType>
struct Node{
	DataType data;
	Node *next;
};

template <class DataType>
class LinkList{
public:
	LinkList();
	LinkList(DataType a[],int n);
	~LinkList();
	void PrintList();
	DataType Get(int i);
private:
	Node<DataType> *first;
};

template <class DataType>
LinkList<DataType>::LinkList(){
	first = new Node<DataType>;
	first -> next = nullptr;
}

template <class DataType>
LinkList<DataType>::LinkList(DataType a[], int n){
	first = new Node<DataType>;
	first -> next = nullptr;
	first -> data = a[n-1];
	for(int i=0;i<n-1;++i){
		Node<DataType> *temp = new Node<DataType>;
		temp -> next = first -> next;
		temp -> data = a[i];
		first->next = temp;
	}
}

template <class DataType>
LinkList<DataType>::~LinkList(){
	while(first != NULL)
	{
		Node<DataType> *q = first;
		first = first->next;
		delete q;
	}
}

template <class DataType>
DataType LinkList<DataType>::Get(int i){
	Node<DataType> *get = first;
	for(int j=1;j<=i;j++)
		get = get->next;
	return get->data;
}

template <class DataType>
void LinkList<DataType>::PrintList(){
	Node<DataType> *prt = first;
	while(prt)
	{
		cout<<prt->data<<endl;
		prt = prt->next;
	}
}

int main(){
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	int n =10;
	LinkList<int> aa(a,n);
//	cout<<aa.Get(4)<<endl;
	aa.PrintList();
	return 0;
}

9.C++数组实现栈(Stack)
因为栈也是线性表,实现起来跟线性表差不多,用数组实现,结构上同样包括一个data数据和一个表示栈顶的 int型的Top,实现过程跟线性表差不多,然后遇到了一个小问题是前面表示数组大小的MaxSize的全局变量没有设置成const的就报错了,后来一想数组是静态申请的,那用变量申请空间肯定不对啊,所以要加上const表示常量。
实现了插入、删除的操作,跟线性表的差别就是插入删除都在一端进行。

#include <iostream>
using namespace std;

const int MaxSize=100;

template<class DataType>
class SeqStack{
public:
	SeqStack(){Top = -1;}
	SeqStack(DataType a[], int n);
	~SeqStack(){};
	void insert(DataType x);
	DataType pop();
	void PrintStack();
private:
	DataType data[MaxSize];
	int Top;
};

template <class DataType>
SeqStack<DataType>::SeqStack(DataType a[],int n){
	Top = -1;
	for(int i=0;i<n;++i){
		data[i]=a[i];
		++Top;}
}

template <class DataType>
void SeqStack<DataType>::insert(DataType x){
	Top++;
	data[Top]=x;
}

template <class DataType>
DataType SeqStack<DataType>::pop(){
	return data[--Top];
}

template <class DataType>
void SeqStack<DataType>::PrintStack(){
	if(Top==-1)
		cout<<"NULL"<<endl;
	else
		for(int i=0;i<=Top;i++)
			cout<<data[i]<<endl;
}

int main()
{	
	int init[10]={1,2,3,4,5,6,7,8,9,10};
	int n = 10;
	int res = 0;
	SeqStack<int> s1;
	SeqStack<int> s2(init,n);
			
	s1.insert(3);
	s1.insert(1);
	s1.insert(2);
	s1.PrintStack();
	res = s1.pop();
	s1.PrintStack();
	cout<<"del "<<res<<endl;
	

	s2.PrintStack();
	res = s2.pop();
	s2.PrintStack();
	cout<<"del "<<res<<endl;
	return 0;
}

10.按照自己需求读取字符串的方法,理论上万能?配合for循环确实是随心所欲

		string::size_type start = str.find_first_of(s,i);     //查找第一个数字或算术符号
		string::size_type end = str.find_first_of(empty,i);   //查找第一个空格
		string tempstr = str.substr(start, end-start);     //取出这一个元素

11.C++链表实现栈

#include <iostream>
using namespace std;

template <class DataType>
struct Node{
	DataType data;
	Node<DataType> *next;
};

template <class DataType>
class Stacklink{
public:
	Stacklink();
	Stacklink(DataType a[], int n);	
	~Stacklink();
	void insert(DataType x);
	DataType pop();
	void PrintStack();
private:
	Node<DataType> *first;	
};

template <class DataType>
Stacklink<DataType>::Stacklink(){
	first = new Node<DataType>;
	first -> next = NULL;
}


template <class DataType>
Stacklink<DataType>::Stacklink(DataType a[],int n){
	first = new Node<DataType>;
	first -> next = NULL;
//	first -> data = a[0];
	Node<DataType> *head = first;
	for(int i=0;i<n;i++){
		Node<DataType> *temp = new Node<DataType>;
		temp -> next = head;
		temp -> data = a[i];
		head = temp;
	}
	first = head;
}

template <class DataType>
Stacklink<DataType>::~Stacklink(){
	while(first){
		Node<DataType> *temp = first;
		first = first->next;
		delete temp;
	}
}

template <class DataType>
void Stacklink<DataType>::insert(DataType x){
	Node<DataType> *p = new Node<DataType>;
	p -> next = first;
	p -> data = x;
	first = p;
}

template <class DataType>
DataType Stacklink<DataType>::pop(){
	Node<DataType> *temp = first;
	first = first->next;
	return temp -> data;
	delete temp;
}

template <class DataType>
void Stacklink<DataType>::PrintStack(){
	Node<DataType> *head = first;
	while(head->next){
		cout<<head->data<<endl;
		head = head->next;
	}	
}


int main()
{
	Stacklink<int> s1;
	s1.insert(1);
	s1.insert(2);
	s1.insert(3);
	
	s1.PrintStack();
	
	int res = s1.pop();

	s1.PrintStack();
	cout<<"res is "<<res<<endl;
	cout<<"-------"<<endl;
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	
	Stacklink<int> s2(a,10);
//	s2.PrintStack();
	s2.insert(11);
	s2.insert(12);
	res = s2.pop();
	s2.PrintStack();
	cout<<"res is "<<res<<endl;
		
	return 0;
}

12.C++数组实现循环队列
思路倒是不难,跟栈的区别在结构上多了一个变量,共两个变量,一个指向队头front,作删除操作,一个指向队尾rear,作插入操作,还有就是循环队列,处理的时候都要和数组最大值去余,最后就是注意判断队列空、满的条件。判断队列空的条件是front取余等于rear取余,比如最初的空队列两个都设为0;判断队列满的条件是rear+1取余后等于front取余,就是空出来了一个front的位置,保证空和满能够被区别开。
最后贴上我写的实现

#include <iostream>
using namespace std;

const int MaxSize = 10;
template <class T>
class QueueLinear{
public:
	QueueLinear(){front=0; rear=0;}
	QueueLinear(T a[],int n);
	bool isfull();
	bool isempty();
	void insert(T x);
	T pop();
	void Print();
//private:
	T data[MaxSize];
	int front;
	int rear;
};

template <class T>
QueueLinear<T>::QueueLinear(T a[],int n){
	front = 0;
	rear = 0;
	for(int i=1;i<=n;i++){
		data[i] = a[i-1];
		rear++;
		}
}

template <class T>
bool QueueLinear<T>::isfull(){
	if((rear+1)%MaxSize==front%MaxSize)
		return true;
	else 
		return false;
}

template <class T>
bool QueueLinear<T>::isempty(){
	if(front%MaxSize==rear%MaxSize)
		return true;
	else
		return false; 
}


template <class T>
void QueueLinear<T>::insert(T x){
	if(!this->isfull())
		data[(++rear)%MaxSize] = x;
	else
		cout<<"can't insert"<<endl;
	}

template <class T>
T QueueLinear<T>::pop(){
	if(this->isempty())
		throw "is empty";
	else
		return data[++front];
}

template <class T>
void QueueLinear<T>::Print(){
	int front1 = front;
	int rear1 = rear;
	while(front1%MaxSize!=rear1%MaxSize){
		cout<<data[++front1%MaxSize]<<endl;
	}
}


int main()
{
//	QueueLinear<int> queue1;
//	cout<<queue1.front<<" "<<queue1.rear<<endl;
//	queue1.insert(1);
//	queue1.insert(2);
//	cout<<queue1.front<<" "<<queue1.rear<<endl;
//	cout<<queue1.data[1]<<endl;
//	int del_1 = queue1.pop();
//	queue1.Print();
//	cout<<"-------------------------"<<endl;
	int a[9] = {1,2,3,4,5,6,7,8,9};
	int n = 9;
	QueueLinear<int> queue2(a,n);
//	cout<<queue2.front<<" "<<queue2.rear<<endl;
	int del = queue2.pop();
	int del_2 = queue2.pop();
	int del_3 = queue2.pop();
	queue2.insert(15);
	queue2.insert(16);
	queue2.insert(17);
//	cout<<queue2.front<<" "<<queue2.rear<<endl;
//	cout<<queue2.data[0]<<" "<<queue2.data[1]<<" "<<queue2.data[2]<<endl;	
	queue2.Print();
	
	return 0;	
}

13.C++链表实现队列,这个感觉比数组实现简单一些,尤其是插入删除操作,时间复杂度O(1)不要太爽,还是注意析构函数手动释放内存,不然会导致内存泄漏。我实现的代码如下

#include <iostream>
using namespace std;

template <class T>
struct Node{
	T data;
	Node *next;
};

template <class T>
class Queuelink{
public:
	Queuelink();
	Queuelink(T a[], int n);
	~Queuelink();
	void insert(T x);
	void pop();
	bool isempty();
	void Print();
private:
	Node<T> *front;
	Node<T> *rear;
};

template <class T>
Queuelink<T>::Queuelink(){
	front = new Node<T>;
	front -> next = nullptr;
	rear = front;
}



template <class T>
Queuelink<T>::Queuelink(T a[],int n){
	front = new Node<T>;
	front -> next = nullptr;
	rear = front;
	Node<T> *head = front;
	for(int i=0;i<n;++i){
		Node<T> *temp = new Node<T>;
		temp -> next = head ->next;
		temp -> data = a[i];
		head -> next = temp;
		head = head -> next;
	}
	rear = head;
}

template <class T>
Queuelink<T>::~Queuelink(){
	while(front){
		Node<T> *temp = front;
		front = front ->next;
		delete temp;
	}
}



template <class T>
void Queuelink<T>::insert(T x){
	Node<T> *temp = new Node<T>;
	temp -> next = rear ->next;
	temp -> data = x;
	rear -> next = temp;
	rear = temp;
}

template <class T>
bool Queuelink<T>::isempty(){
	if(front->next==nullptr)
		return true;
	else 
		return false;
}

template <class T>
void Queuelink<T>::pop(){
	if(this->isempty())
			cout<<"is empty,can't pop"<<endl;
		else
			front -> next = front->next->next;
	}

	template <class T>
	void Queuelink<T>::Print(){
		Node<T> *head = front->next;
	while(head){
		cout<<head->data<<endl;
		head = head->next;
	}
}

int main()
{
	Queuelink<int> queue1;
	queue1.insert(1);
	queue1.insert(2);
	queue1.insert(3);
	queue1.insert(4);
	queue1.pop();
	queue1.Print();

	cout<<"-------------------------------"<<endl;
	int a[8]= {1,2,3,4,5,6,7,8};
	int n = 8;
	Queuelink<int> queue2(a,n);
	queue2.pop();
	queue2.Print();
	return 0;
}

C++实现二叉树的创建,先中后序遍历
写出来编译没错,一运行就报错“段错误,核心已转储”,然后一直找不到错误,还不如报错来得实在。。。后来一步一步的调试才发现是析构函数出了问题,迭代delete删除内存搞错了一个判断条件。。。最后终于实现了二叉树的创建,思路和前面都差不多,要注意的是创建过程中createbintree函数,输入的形参是指针的引用,因为我在函数里修改了传入的参数,要让他同时对实参产生效果就要输入的引用。

#include <iostream>
using namespace std;

template <class T>
struct TreeNode{
	T data;
	TreeNode *left;
	TreeNode *right;
};

template <class T>
class BinTree{
public:
	BinTree();
	~BinTree();
	void createbintree(TreeNode<T> * &x);//这里是指针引用
	void clear(TreeNode<T> * x);
	bool isempty();
	void front_erg(TreeNode<T> *x);
	void mid_erg(TreeNode<T> *x);
	void behind_erg(TreeNode<T> *x);
	TreeNode<T> *root;	
};


template <class T>
BinTree<T>::BinTree(){
//	cout<<"this is create"<<endl;
	root = nullptr;
//	createbintree(root);
}

template <class T>
BinTree<T>::~BinTree(){
//	cout<<"this is ~BinTree"<<endl;
	clear(root);
}

template <class T>
void BinTree<T>::clear(TreeNode<T> * x){
	if(x==NULL)
		return;
	else
		clear(x->left);
		clear(x->right);
		delete x;

/*	if(root==NULL)
		return;
	else
		delete root;
		root = NULL;
*/
}


template <class T>
void BinTree<T>::createbintree(TreeNode<T> * &x){
	char ch;
	if(cin)
		cin>>ch;
	else
		ch = '#';
	if(ch=='#'){
		x = NULL;
	}
	else{
		x = new TreeNode<T>;
		x -> data = ch;
		createbintree(x->left);
		createbintree(x->right);
	}
}



template <class T>
bool BinTree<T>::isempty(){
	if(root==nullptr)
		return true;
	else
		return false;
}


template <class T>
void BinTree<T>::front_erg(TreeNode<T> *x){
	if(x){
		cout<<x->data<<" ";
		front_erg(x->left);
		front_erg(x->right);
	}
}



template <class T>
void BinTree<T>::mid_erg(TreeNode<T> *x){
	if(x){
		mid_erg(x->left);
		cout<<x->data<<" ";
		mid_erg(x->right);
	}
}




template <class T>
void BinTree<T>::behind_erg(TreeNode<T> *x){
	if(x){
		behind_erg(x->left);
		behind_erg(x->right);
		cout<<x->data<<" ";
	}
}


int main()
{
	BinTree<char> bintree1;
	bintree1.createbintree(bintree1.root);
	cout<<"there is output"<<endl;
	bintree1.front_erg(bintree1.root);
	cout<<endl;
	bintree1.mid_erg(bintree1.root);
	cout<<endl;
	bintree1.behind_erg(bintree1.root);
	cout<<flush;
//	cout<<bintree1.root->data<<endl;
	return 0;	
}

平衡二叉树的实现C++

#include <iostream>
using namespace std;

template <class T>
struct Node{
    T data;
    Node<T> *left;
    Node<T> *right;
    int height;
};

template <class T>
class AVL{
public:
    AVL();
    ~AVL();
    void clear(Node<T> *temp);
    int getheight(Node<T> *temp);
    Node<T> * SingleLeftRotation(Node<T> *temp);
    Node<T> * SingleRightRotation(Node<T> *temp);
    Node<T> * DoubleLeftRightRotation(Node<T> * temp);
    Node<T> * DoubleRightLeftRotation(Node<T> * temp);
    Node<T> * insert(Node<T> *temp,T x);
    void pre_order(Node<T> * temp);
    void mid_order(Node<T> * temp);   
//private树的结构
    Node<T>  *root;//树根
};

//构造函数,构造空树,令树根为空指针,没有头结点
template <class T>
AVL<T>::AVL(){
    root = nullptr;
}


//析构函数,delete申请的空间
template <class T>
AVL<T>::~AVL(){
    if(!root)
        return;
    else
        clear(root);
}
//析构函数调用,清除内存
template <class T>
void AVL<T>::clear(Node<T> *temp){
    if(!temp)
        return;
    else{
        clear(temp->left);  
        clear(temp->right);
        delete temp;
    }
}

//求指定指针对应的树的高度
template <class T>
int AVL<T>::getheight(Node<T> *temp){
    int left_hgt = 0;
    int right_hgt = 0;
    int hgt = 0;
    if(!temp)
        return 0;
    else{
        left_hgt = getheight(temp->left);
        right_hgt = getheight(temp->right);
        hgt = max(left_hgt,right_hgt);
        return hgt+1;
        }
}

//插入结点的函数,关键, 返回指针类型
template <class T>
Node<T> *AVL<T>::insert(Node<T> *temp,T x){
    if(!temp){
            temp = new Node<T>;
            temp->data = x;
            temp->left = nullptr;
            temp->right = nullptr;
            temp->height = 1;
            }
//判断是否需要进行AVL调整,分别使用对应的 左单旋 右单旋 左右双旋和右左单旋进行调整
    else if(x<temp->data){
        temp->left = insert(temp->left,x);
        if(getheight(temp->left)-getheight(temp->right)==2){
            if(x<temp->left->data)
                temp = SingleLeftRotation(temp);
            else
                temp = DoubleLeftRightRotation(temp);
            }
        }
    else if(x>temp->data){
        temp->right = insert(temp->right,x);
        if(getheight(temp->right)-getheight(temp->left)==2){
            if(x>temp->right->data)
                temp = SingleRightRotation(temp);
            else
                temp = DoubleRightLeftRotation(temp);
        }
    }
    else ;
//更新结点高度
    temp->height=max(getheight(temp->left),getheight(temp->right))+1;
    return temp;
}
//左单旋
template <class T>
Node<T> * AVL<T>::SingleLeftRotation(Node<T> *temp){
    Node<T> * var = temp->left;
    temp->left = var->right;
    var -> right = temp;
    temp ->height = max(getheight(temp->left),getheight(temp->right))+1;
    var ->height = max(getheight(var->left),getheight(var->right))+1;
    return var;
}
//右单旋
template <class T>
Node<T> *AVL<T>::SingleRightRotation(Node<T> *temp){
    Node<T> * var = temp ->right;
    temp->right = var->left;
    var->left = temp;
    temp->height = max(getheight(temp->left),getheight(temp->right))+1;
    var->height = max(getheight(var->left),getheight(var->right))+1;
    return var;
}
//左右双旋
template <class T>
Node<T> * AVL<T>::DoubleLeftRightRotation(Node<T> * temp){
    temp->left = SingleRightRotation(temp->left);
    return SingleLeftRotation(temp);
}
//右左双旋
template <class T>
Node<T> * AVL<T>::DoubleRightLeftRotation(Node<T> * temp){
    temp->right = SingleLeftRotation(temp->right);
    return SingleRightRotation(temp);
}

template <class T>
void AVL<T>::pre_order(Node<T> * temp){
    if(!temp)
        return;
    else{
        cout<<temp->data<<endl;
        pre_order(temp->left);
        pre_order(temp->right);
    }
}

template <class T>
void AVL<T>::mid_order(Node<T> * temp){
    if(!temp)
        return;
    else
        mid_order(temp->left);
        cout<<temp->data<<endl;
        mid_order(temp->right);
}

//外部函数,求两个数中较大的一个
int max(int x,int y){
    return x>y? x:y;
}

int main()
{
    AVL<int> avltree;
    avltree.root = avltree.insert(avltree.root,5);
    avltree.root = avltree.insert(avltree.root,10);
    avltree.root = avltree.insert(avltree.root,15);
    avltree.root = avltree.insert(avltree.root,20);
    if(avltree.root)
        cout<<"true"<<endl;
    else
        cout<<"false"<<endl;
    avltree.pre_order(avltree.root);
    cout<<"-----------------------------"<<endl;
    avltree.mid_order(avltree.root);
    return 0;
}

C++实现最大堆,保持它是完全二叉树,并且由于是完全二叉树,所以用数组表示就会很简单,注意根节点的序号要从1开始,节点N的左儿子序号是2N,右儿子序号是2N+1,0序号放一个最大值,起到“哨兵”的作用(开始不知道为什么,后来写到插入的时候就知道了,哨兵是循环判断的终止条件),我这里用的是INT_MAX(在<limites.h>头文件中)。插入过程是先插入到数组最后一个节点,然后向上过滤,一直找到合适的位置停止。删除的过程我感觉还是有点技巧的,不是简单的把根节点和最后节点换位置然后向下过滤,而是把最后的节点拿出来,不断和下面的节点比较找到合适位置,然后比较过的节点上移,其实道理和更换然后向下过滤是一样的,不过好实现一些。

#include <iostream>
#include <limits.h>
using namespace std;

const int MaxSize = 100;
class Maxheap{
public:
    Maxheap();
//    ~Maxheap();
    bool isfull();
    bool insert(int x);
    bool isempty();
    int pop();
    void travel();
private:
    int heap[MaxSize];
    int length;
};

//构造函数
Maxheap::Maxheap(){
    heap[0] = INT_MAX;
    length = 1;
}

bool Maxheap::isfull(){
    if(length==MaxSize)
        return true; 
    else
        return false;
}

bool Maxheap::insert(int x){
    if(this->isfull())
        return false;
    else{
        heap[length]=x;
        int length1 = length;
        for(int temp=length/2;heap[temp]<heap[length1];temp/=2){
            int temp1 = heap[temp];
            heap[temp] = heap[length1];
            heap[length1] = temp1;
	    length1 = temp;
        }
        length++;
        return true;
    }
}

bool Maxheap::isempty(){
    if(length==1)
        return true;
    else
        return false;
}

int Maxheap::pop(){
    if(length==1)
        return -1;
    else{
	int max_item = heap[1];
        int temp = heap[length-1];
	int child,parent;
        length--;
        for(parent=1;parent*2<length;parent=child){
            child = parent*2;
            if(child+1<length && heap[child]<heap[child+1])
                child++;
            if(temp>=heap[child])
                break;
            else
                heap[parent] = heap[child];
            }
        heap[parent] = temp;
	return max_item;
    }
}

void Maxheap::travel(){
    for(int i=1;i<length;i++)
        cout<<heap[i]<<endl;
}

int main()
{
    Maxheap maxheap;
    maxheap.insert(5);
    maxheap.insert(10);
    maxheap.insert(15);
    maxheap.insert(30);
    maxheap.insert(3);
    int res = maxheap.pop();
    int res1 = maxheap.pop();
//    int res2 = maxheap.pop();
    maxheap.travel();
    return 0;
}

C++实现图
首先是临接矩阵实现,这里终于找到机会熟悉了一下多维数组,其实就是一个二维指针,外层是一个指向指针数组的指针,内层是一个指向数组的指针,然后我觉得图的结构相对来说最麻烦,最重要的两个操作集是DFS深度优先搜索和BFS广度优先搜索,深度优先搜索肯定迭代好实现,广度优先搜索用到了一个队列来辅助进行,实现代码如下,这个地方真是改了好久,难受

#include <iostream>
#include <queue>
using namespace std;
#define SIZE 10

int MaxVertex = SIZE;
class Graph{
public:
    Graph();
    ~Graph();
    void InsertVertex(int x);
    int GetVertex(int x);
    void InsertEdge(int Node1,int Node2,int x);
    void DeleteEdge(int Node1,int NOde2);
    bool IsEmpty();
    void DFS(int x);
    void BFS(int x);
//private:
    int *Vertex;
    int **Edge;
    int NumVertex;
    int NumEdge;
    bool * visited1;
    bool * visited2;
    queue<int> que;
};

Graph::Graph(){
    int MaxVertex = SIZE;
    NumVertex = NumEdge = 0;
    Vertex = new int[MaxVertex];
    visited1 = new bool[MaxVertex];
    visited2 = new bool[MaxVertex];
    for(int i=0;i<MaxVertex;++i){
        visited1[i]=false;
        visited2[i]=false;}
    Edge = new int*[MaxVertex];
    for(int i=0;i<MaxVertex;++i)
        Edge[i] = new int[MaxVertex];
    for(int i=0;i<MaxVertex;++i)
        for(int j=0;j<MaxVertex;++j)
            Edge[i][j]=0;
}

Graph::~Graph(){
    delete[] Vertex;
    Vertex = nullptr;
    for(int i=0;i<MaxVertex;++i){
        delete[] Edge[i];
        Edge[i] = nullptr;
        }
    delete[] Edge;
    Edge = nullptr;
    NumVertex = 0;
}

void Graph::InsertVertex(int x){
    if(NumVertex>=MaxVertex)
        return;
    else
        Vertex[NumVertex++] = x;
}

int Graph::GetVertex(int x){
    for(int i=0;i<NumVertex;++i){
        if(Vertex[i]==x)
            return i;
    }
    return -1;
}

void Graph::InsertEdge(int Node1, int Node2, int x){
    int p1 = GetVertex(Node1);
    int p2 = GetVertex(Node2);
    if(p1==-1 || p2==-1)
        return;
    Edge[p1][p2] = x;
    Edge[p2][p1] = x;
    NumEdge++;
}


void Graph::DeleteEdge(int Node1, int Node2){
    int p1 = GetVertex(Node1);
    int p2 = GetVertex(Node2);
    if(p1==-1 || p2==-1)
        return;
    Edge[Node1][Node2] = Edge[Node2][Node1] = 0;
    NumEdge--;
}

bool Graph::IsEmpty(){
    if(NumVertex)
        return false;
    else
        return true;
}

void Graph::DFS(int s){
    int p = GetVertex(s);
    cout<<"visit Node "<<s<<endl;
    visited1[p] = true;
    for(int i=0;i<NumVertex;++i){
        if(!visited1[i] && Edge[i][p]!=0)
            DFS(Vertex[i]);            
    }
}

/*广度优先搜索,这里自己写了一下感觉递归实现有难度。。后来看书上没有用递归,想了一下广度优先搜索的话循环感觉确实好
实现一些,因为迭代是一个堆栈,先进后出,更符合深度优先算法,于是广度优先用循环实现配合队列就比较好*/
void Graph::BFS(int s){
    int p = GetVertex(s);
    cout<<"visit Node "<<s<<endl;//访问该节点
    que.push(p);
    visited2[p] = true;
    while(!que.empty()){
        int temp = que.front();
        que.pop();
        for(int i=0;i<NumVertex;++i){
            if(!visited2[i] && Edge[temp][i]!=0){
                cout<<"visit Node "<<Vertex[i]<<endl;//循环访问弹出节点的相关节点
                visited2[i]=true;
                que.push(Vertex[i]);}//循环将访问过的放入队列
    	} 
    }
}

int main()
{
    Graph graph1; 
    graph1.InsertVertex(1);
    graph1.InsertVertex(2);
    graph1.InsertVertex(3);
    graph1.InsertVertex(4);
    graph1.InsertVertex(5);
    graph1.InsertVertex(6);
    graph1.InsertEdge(1,2,1);
    graph1.InsertEdge(2,3,1);
    graph1.InsertEdge(2,4,1);
    graph1.InsertEdge(3,4,1);
    graph1.InsertEdge(1,5,1);
    graph1.InsertEdge(1,6,1);
    graph1.InsertEdge(5,6,1);
    cout<<graph1.NumVertex<<endl;
    cout<<graph1.NumEdge<<endl;	
//    graph1.DFS(2);
    graph1.BFS(2);
    return 0;   
}

图的最短路径问题,单源最短路问题

Djikstra算法:
思路是,首先考虑一个源点,找到它到其他所有点的最短路径,先列一个初始矩阵,保存了源点到其他点的直接距离(也可以全部写成无穷大,会更新的,当然自己要写成0)的列表A,然后一个包含所有点的索引的列表Q,一个空列表S,然后判断Q是否为空每次循环执行:找到S中最小的值,把它对应的点从Q中删除,加入到S中,再对这个点的其他所有相连的点考虑,该点的距离加上它们之间的距离是否小于A中原本距离,如果小于就把A中的距离更新成较小的距离,接着进入下一次循环,直到循环结束,产生的新的A就是最终结果,如果想获得路径,就新建一个A_pre,保存了每个节点的上一个节点是什么,初始都设成源节点,每次循环的时候如果某个节点的距离发生变化,就意味着它的上一个节点也发生了变化,因此就把这个A_pre里的值更新即可,最后逐个节点迭代就能找到完整的路径了
这里是python实现

#图的最小路径dijsktra算法
def dijkstra(graph, s, target):
    # 判断图是否为空,如果为空直接退出
    if graph is None:
        return None
    dist = [MAX]*len(graph)
    dist[s] = 0
    pre = [s for i in range(len(graph))]
    S = []
    path = []
    Q = [i for i in range(len(graph))]

    dist_init = [i for i in graph[s]]
    while Q:
#        u_dist = min([d for v, d in enumerate(dist_init) if v in Q])
#        u = dist_init.index(u_dist)
        d_temp = MAX
        v_temp = -1
        for v,d in enumerate(dist_init):
            if v in Q and d < d_temp:
                d_temp = d
                v_temp = v
        u = v_temp
        S.append(u)
        Q.remove(u)

        for v, d in enumerate(graph[u]):
            if 0 < d < MAX:
                if dist[v] > dist[u]+d:
                    dist[v] = dist[u]+d
                    dist_init[v] = dist[v]
                    pre[v] = u


    #按照pre的顺序迭代找到路径
    target_1 = target
    while target_1 != s:
        path.append(target_1)
        target_1 = pre[target_1]
    path.append(s)
    return dist[target], path[::-1]

排序
C++实现简单选择排序,这么简单的东西我居然看了好久!!! 主要就卡在了发现函数不起作用上面,后来发现是数组作为形参的时候是引用传递,而vector作为形参的时候是值传递。。。这个地方好坑呀

二叉树性质
1.一个二叉树第i层最大节点数 2^(i-1)
2.一个i层二叉树最多节点个数是2^i-1, 等比数列求和
3.对于任何非空二叉树,n0叶节点个数,n2度为2的非叶节点个数,n1度为1的节点个数,则有n0 = n2 + 1
推导

n = n0 + n1 + n2
n-1 = n1 + 2 * n2
联立,消n1
解得   n0 = n2 +1

4.具有n个结点的完全二叉树深度k为 log2(n)取下端 +1

二叉搜索树找最大、找最小、插入、删除某个节点操作
找最大,最大的点一定在最右端,那么就迭代找点,一直找右子树,若右子树为空,那么该点就是最大点。边界条件是判断右子树为空,则返回该点,否则继续迭代在右子树里寻找

找最小,同理,最小的点一定在最左端,那么边界条件是判断左子树为空,返回该点,否则继续迭代再左子树里寻找。

插入很简单,类似于找节点,递归实现。边界条件是树为空,新建结点,设左右子树指向NALL,然后值就是要插入的值。不满足边界条件的时候,判断小于节点值,迭代插入左子树,大于节点值,迭代插入右子树

删除有点复杂,还是递归实现,如果树为空,则返回失败,找不到。如果树不为空,若小于节点值,迭代删除左子树,若大于节点值,迭代删除右子树,else(就是等于的时候),这时分两种情况,1是这个点有两个子树,那么找到右子树的最小值,替换该节点。2是有一个或者没有子树,再分两种情况:1是若没有左子树,那么直接让该节点等于右子树即可,2是若有左子树,让它等于左子树

创建二叉树补充

之前的写法是要求一直输入,用ctrl+D停止输入,在char型的没问题,但是用int型会多一个无定义的数,因为把ctrl+D也作为一个输入了,于是考虑固定容器生成二叉树,注意这里容器是先序遍历,将-1作为空结点,并且叶节点的-1也要写出来,且一定要包含-1,例如
在这里插入图片描述

void createBinaryTree(BinaryTreeNode* &root,vector<int> nums,int &index)
{
    if(index>=nums.size())
        return;
    int input = nums[index];
    if(input == -1)
    {
        root = nullptr;
    }
    else
    {
        root = new BinaryTreeNode;
        root->m_Value = input;
        index++;
        createBinaryTree(root->m_pLeft,nums,index);
        index++;
        createBinaryTree(root->m_pRight,nums,index);
    }
}

采用递归的方式,每次处理index++,注意index要传引用

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值