左程云算法基础班整理(二)(C++版)

左程云算法基础班整理(二)

本系列文章是整理左程云算法基础班的视频,将Java代码翻译成C++代码。并且记录一些核心思想

1.单链表

struct ListNode
{
    int val;
    ListNode* next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode* next) : val(x), next(next) {}
};

2.双链表

struct DoubleListNode
{
    int val;
    DoubleListNode* next;
    DoubleListNode* prev;
    DoubleListNode():val(0),next(nullptr),prev(nullptr){}
    DoubleListNode(int x):val(x),next(nullptr),prev(nullptr){}
    DoubleListNode(int x,DoubleListNode* next,DoubleListNode* prev):val(x),next(next),prev(prev){}
};

1.反转链表

翻转单链表

首先是翻转单链表,链表题全是coding题,因此一定要细心,谨慎。

反转链表,(1)先记录环境,之后再设置头部两个指针的指向。
(2)pre和head都向右移。
内部的结构一般是首尾相连的。next->head.next->pre->head

ListNode* reverseLinkedList(ListNode* head)
{
    ListNode* next = nullptr;	//记前面的环境
    ListNode* pre = nullptr;	//记下面的环境
    while (head)
    {
        //完成翻转
        next = head->next;
        head->next = pre;
        //为了循环,往后移
        pre = head;
        head = next;
    }
    return pre;
}

在这里插入图片描述

翻转双链表
DoubleListNode* reverseDoubleLinkedList(DoubleListNode* head)
{
    DoubleListNode* next = nullptr;
    DoubleListNode* pre = nullptr;
    while (head)
    {
        //记一下环境
        next = head->next;
        
        //翻转的核心,head的下指针往前指,前指针往后指
        head->next = pre;
        head->prev = next;
        
        //为了循环
        pre = head;
        
        head = next;
    }
    return pre;
}

2.删除链表中指定的值(剑指offer第18题)

讲解时间,第二课45分钟左右开始讲解。
返回值要是一个节点,因为在删除时,是有可能将头结点删除掉的。

#include <iostream>
using namespace std;

struct ListNode
{
	int value;
	ListNode* next;
	ListNode():value(0),next(nullptr){}
	ListNode(int x):value(x),next(nullptr){}
	ListNode(int x, ListNode* next) :value(x), next(next) {};
};
ListNode* DeleteGivenValue(ListNode* head,int num)
{
	//先看看头部要删多少,找到第一个不需要删的节点,当做头。
	while (head!=nullptr)//这一个是来遍历
	{
		if (head->value != num)//当不需要删的时候,break出来,此时的head节点就是将来要返回的节点。
		{
			break;
		}
		head = head->next;
	}
	//head此时来到 第一个不需要删的位置。因此可以将其保存起来,另申请一个指针用来遍历。
	ListNode* pre = head;//记录pre是为了要找到前一个节点的向下的指针
	ListNode* cur = head;//申请个cur变量,此时是第一个不需要删的位置,因此head就可以不用动了,遍历的时候只需要动cur变量即可。
	while (cur != nullptr)
	{
		if (cur->value == num)
		{
			pre->next = cur->next;
		}
		else {
			pre = cur;//这里直接pre后移的话就产生错误。
		}
		cur = cur->next;
	}
	return head;
}

c++的节点要自己delete掉,要不然会发生内存泄露。
但是翻遍了各大题解,都没有进行delete掉该节点的题解。。。。。。

3.双向链表实现栈和队列

#include<iostream>
using namespace std;

template<class T>
struct DoubleLinkList
{
	T val;
	DoubleLinkList* prev;
	DoubleLinkList* next;

	DoubleLinkList():val(0),prev(nullptr),next(nullptr){}
	DoubleLinkList(T x):val(x),prev(nullptr),next(nullptr){}
	DoubleLinkList(T x,DoubleLinkList* next,DoubleLinkList* prev):val(x),next(next),prev(prev){}
};

template<class T> 
class DoubleEndsQueue
{
public:
	void addFromHead(T value)
	{
		DoubleLinkList* cur = new DoubleLinkList(value);//待加入的节点
		
		if (head == nullptr)//之前为空
		{
			head = cur;
			tail = cur;
		}
		else//
		{
			cur->next = head;
			head->prev = cur;
			head = cur;
		}
	}
	void addFromBottom(int value)
	{
		DoubleLinkList* cur = new DoubleLinkList(value);
		if (head == nullptr)
		{
			head = cur;
			tail = cur;
		}
		else
		{
			cur->prev = tail;
			tail->next = cur;
			tail = cur;
		}
	}
	T popFromHead()
	{
		if (head == nullptr)
		{
			return nullptr;
		}
		DoubleLinkList* cur = head;//申请一个指针,指向头结点
		if (head == tail)//只有一个节点,那出去一个就变成没有节点了,直接都置空
		{
			head = nullptr;
			tail = nullptr;
		}
		else
		{
			head = head->next;
			head->prev = nullptr;
			cur->next = nullptr;
		}
		return cur->val;
	}
	T popFromBottom()
	{
		if (head == nullptr)
		{
			return nullptr;
		}
		DoubleLinkList* cur = tail;
		if (head == tail)
		{
			head = nullptr;
			tail = nullptr;
		}
		else
		{
			tail = tail->prev;
			tail->next = nullptr;
			cur->prev = nullptr;
		}
		return cur->val;
	}
	bool isEmpty()
	{
		return head == nullptr;
	}
private:
	//所有数据的头和尾,两个节点指针
	DoubleLinkList* head;
	DoubleLinkList* tail;
};

template<class T>
class MyStack
{
public:
	MyStack()
	{
		queue = new DoubleEndsQueue();
	}
	void push(int value)
	{
		queue->addFromHead(value);
	}
	T pop()
	{
		return queue->popFromHead();
	}
	bool isEmpty()
	{
		return queue->isEmpty();
	}

private:
	DoubleEndsQueue* queue;
};

template<class T>
class MyQueue
{
public:
	MyQueue()
	{
		queue = new DoubleEndsQueue();
	}
	T poll()
	{
		return queue->popFromBottom();
	}
	void push()
	{
		return queue->addFromHead();
	}
	bool isEmpty()
	{
		return queue->isEmpty();
	}
private:
	DoubleEndsQueue* queue;
};

4.怎么用数组实现不超过固定大小的队列和栈

栈:正常使用

#include<iostream>
#include<assert.h>
#include<array>
using namespace std;

//用数组实现栈
class MyStack
{
public:
	MyStack(int limit)
	{
		this->limit = limit;
		arr = new int[limit];
		index = 0;
	}
	~MyStack() 
	{
		delete[] arr;
	}
	void push(int val)
	{
		if (!isFullStack())
		{
			arr[index++] = val;
		}
		else
		{
			cout << "the stack is full !" << endl;
			assert(0);
		}
	}
	void pop()
	{
		if (!isEmpty())
		{
			index--;
		}
		else
		{
			cout << "the stack is empty !" << endl;
			assert(0);
		}
	}
	int top()
	{
		if (!isEmpty())
		{
			return arr[index];
		}
		else
		{
			cout << "the stack is empty !" << endl;
			assert(0);//如果栈为空那么必须终止程序,reuturn 返回的会是乱码,push和pop不需要这个操作是因为他们没有返回值,所以如果不满足执行的条件只需要在终端上显示出现异常就行。
		}
	}
	bool isEmpty() const
	{
		return index == 0;
	}
	bool isFullStack() const
	{
		return index == 9;
	}
private:
	int limit;
	int* arr;
	int index;
};



int main()
{
	
	MyStack stack(3);
	cout << stack.isEmpty() << endl;
	//cout << stack.top() << endl;
	stack.push(5);
	cout << stack.isEmpty() << endl;
	int b = stack.top();
	cout << b << endl;
	cout << stack.isEmpty() << endl;
	

	/*
	MyQueue queue(3);
	cout << queue.isEmpty() << endl;
	queue.push(2);
	cout << queue.isEmpty() << endl;
	queue.push(1);
	queue.push(9);
	*/


	system("pause");
	return 0;
}

队列:环形数组
精髓点在于,不是根据pushi和polli的相对位置关系讨论能否进出队,而是用size值。多一个变量,省好多事。

class MyQueue
{
public:
	MyQueue(int limit)
	{
		this->limit= limit;
		arr = new int[this->limit];
		size = 0;
		pushi = 0;
		polli = 0;
	}
	~MyQueue()
	{
		delete[] arr;
	}
	void push(int value)
	{
		if (size == this->limit)
		{
			cout << "栈满了,不能再加了!!!" << endl;
			assert(0);
		}
		else
		{
			size++;
			arr[pushi] = value;
			pushi = this->nextIndex(pushi);
		}
	}
	int pop()
	{
		if (size == 0)
		{
			cout << "栈为空,不能再拿了!!!" << endl;
			assert(0);
		}
		size--;
		int ans = arr[polli];
		polli = this->nextIndex(polli);
	}

	bool isEmpty() const
	{
		return size == 0;
	}
	int nextIndex(int i)
	{
		return i < this->limit - 1 ? i + 1 : 0;
	}
private:
	int* arr;
	int limit;
	int pushi;//进来的时候放哪,坐标
	int polli;//出去的时候放哪,坐标
	int size;//因为是环形数组,所以不用考虑pushi和polli的位置,只要size不为0,就是里面有值,也就是没追上
};

int main()
{
	/*
	MyStack stack;
	cout << stack.isEmpty() << endl;
	//cout << stack.top() << endl;
	stack.push(5);
	cout << stack.isEmpty() << endl;
	int b = stack.top();
	cout << b << endl;
	cout << stack.isEmpty() << endl;
	*/

	MyQueue queue(3);
	cout << queue.isEmpty() << endl;
	queue.push(2);
	cout << queue.isEmpty() << endl;
	queue.push(1);
	queue.push(9);


	system("pause");
	return 0;
}

5.最小栈(leetcode155题)

实现一个特殊的栈,在基本功能的基础上,再实现返回栈中最小元素的功能
1.pop、push、getMin操作的时间复杂度都是O(1)
2.设计的栈类型可以使用现成的栈结构

这道题很简单,使用辅助栈即可。

class MinStack {
public:
    MinStack() {
        help.push(INT_MAX);
    }
    
    void push(int val) {
        a.push(val);
        int mini = min(val,help.top());
        help.push(mini);
    }
    
    void pop() {
        a.pop();
        help.pop();
    }
    
    int top() {
        return a.top();
    }
    
    int getMin() {
        return help.top();
    }
    stack<int> a;
    stack<int> help;
};

6.用栈实现队列

队列是先进先出的,栈是先进后出的。
两个栈倒一下,顺序就和队列是一样的了。
可以分成一个主栈,一个专门用来进行倒的辅助栈。
再倒的时候要注意一些条件:
1.辅助栈为空,才能往里倒,里面还有数据则不能倒
2.往辅助栈里倒就直接倒完,不能倒一半。
在这里插入图片描述

class MyQueue {
public:
    stack<int> a;
    stack<int> help;
    MyQueue() {

    }
    void helpToA()//倒的函数
    {
        while(!help.empty())//倒就要倒干净
        {
            a.push(help.top());
            help.pop();
        }
    }
    void push(int x) {
       help.push(x);
    }
    
    int pop() {
        while(a.empty())//主栈空了才能往里倒
        {
            helpToA();
        }
        int r = a.top();
        a.pop();
        return r;
    }
    
    int peek() 
    {
        while(a.empty())//主栈空了才能往里倒
        {
            helpToA();
        }
        int r = a.top();
        return r;
    }
    
    bool empty() {
        return a.empty()&&help.empty();
    }
};

7.用队列实现栈

左神原话,这题很好想,和弱智一样。。。。。。但是我就没想起来。。。。
两个队列,都是从头进,从尾出。
两个队列,一个辅助队列,一个主队列,主队列存储所有数据,辅助队列只是帮助进队列所用,所以最终形态里辅助队列是没有值的,因此直接看主队列有没有值就可以判空了。
在这里插入图片描述
在这里插入图片描述
下一次help变data,data变help。

class MyStack {
public:
    queue<int> a;
    queue<int> help;
    MyStack() {

    }
    void push(int x) {
        help.push(x);//先压入辅助队列
        while(!a.empty())//当主队列不空时
        {
            help.push(a.front());//往辅助队列中压入主队列的对头
            a.pop();//主队列队头出去
        }
        swap(a,help);//主队列为空,则直接交换辅助队列和主队列
    }
    
    int pop() {
        int r = a.front();
        a.pop();
        return r;
    } 
    int top() {
        int r = a.front();
        return r;
    }
    bool empty() {
        return a.empty();
    }
};

8. 递归方法实现求数组中最大值(用递归实现二分,递归初体验)

#include<iostream>
using namespace std;


int BSsearchmax(int a[], int L, int R)
{
	if (L == R)//递归要写递归终止条件
	{
		return a[L];
	}
	int mid = L + ((R - L) >> 1);
	return max(BSsearchmax(a, L, mid), BSsearchmax(a, mid + 1, R));//递归要划分子分支调用递归函数
}


int main()
{
	int a[9] = { 0,1,3232,41,2121,41,2121,4232,111 };
	cout << BSsearchmax(a, 0, 8) << endl;;

	return 0;
}

9.哈希表的基本使用(c++)

9.1 头文件
#include<unordered_map>
9.2 定义一个哈希表

假设键和值都是int型的

unordered_map<int,int> a;
9.3 建立

哈希表的建立有3种方法

a[1]=3;
hash.insert<make_pair(1,3)>;
hash.insert({{1,3},{2,4}});
9.4 迭代器
unordered_map<int,int>::iterator it;
9.5 利用迭代器访问键或者值
it->first;//访问键
it->second;//访问值
9.6查找

注意,find函数返回的是元素位置所在的迭代器,如果没找到,则返回的是a.end();

it=a.find(1);
9.7包含

包含返回的是int值,记录找到的元素的个数。注意contain函数是string用的,哈希表和vector没有contain

a.count(1);
9.8修改哈希表
a[1]=4;
9.9清除哈希表

清除哈希表要注意迭代器失效的问题

a.erase(1);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值