重刷线性表

一、仿写vector

class Array {
public:
	Array(int size = 10):capacity(size), cur(0) {
		p_arr = new int[size]();
	}
	~Array() {
		// 若只是释放,那p_arr还是指向堆内存,成为野指针
		// 没必要if(p_arr == nullptr),因为delete nullptr就是空操作,不报错
		delete[] p_arr;
		p_arr = nullptr;
	}

	void push_back(int val) {
		if (cur == capacity) {
			expand(capacity);
		}
		p_arr[cur++] = val;
	}

	void pop_back() {
		if (cur == 0) {
			return;
		}
		cur--;
	}

	// 按位置插入
	void insert(int pos, int val) {
		if (pos < 0 || pos > cur) {
			throw "pos out of range";
		}
		if (cur == capacity) {
			expand(capacity);
		}
		for (int i = cur - 1; i >= pos; i--) {
			p_arr[i + 1] = p_arr[i];
		}
		p_arr[pos] = val;
		cur++;
	}
	// 按位置删除
	void erase(int pos) {
		if (pos < 0 || pos >= cur) {
			throw "pos out of range";
		}
		for (int i = pos + 1; i < cur; i++) {
			p_arr[i-1] = p_arr[i];
		}
		cur--;
	}

	// 按值查找,返回索引
	int find(int val) {
		for (int i = 0; i < cur; i++) {
			if (p_arr[i] == val) {
				return i;
			}
		}
		return -1;
	}

	void show() {
		for (int i = 0; i < cur; i++) {
			cout << p_arr[i] << " ";
		}
		cout << endl;
	}
private:
	void expand(int size) {
		int* tmp_arr = new int[2*size]();
		// 不涉及深拷贝可以使用memcpy
		// memcpy(tmp_arr, p_arr, sizeof(int) * capacity);
		for (int i = 0; i < cur; i++) {
			tmp_arr[i] = p_arr[i];
		}
		delete[] p_arr;
		p_arr = tmp_arr;
		capacity = size;
	}
	int* p_arr;
	int capacity;
	int cur;       // 数组内元素个数
};

二、数组中偶数放左边,奇数放右边

// 偶数放在左边,奇数放在右边
void adjust_array(int arr[], int size) {
	int* left = arr;
	int* right = arr + size - 1;
	while (left < right) {
		while (left < right && (*left & 0x1) == 0) {
			left++;
		}
		while (left < right && (*right & 0x1) != 0) {
			right--;
		}
		// 不写if程序结果也正确,如果不写if,left==right时也会进行交换
		// 不写if,程序在交换时满足left<=right
		if (left < right) {
			int tmp = *left;
			*left = *right;
			*right = tmp;
			left++;
			right--;
		}
	}
}

三、移除指定元素

移除元素

// 移除指定元素,返回长度
int removeElement(vector<int>& nums, int val) {
	int left = 0;
	int right = nums.size() - 1;
	while (left <= right) {
		if (nums[right] == val) {
			right--;
			continue;
		}
		if (nums[left] == val) {
			nums[left] = nums[right];
			right--;
		}
		left++;
		// right找到非val的元素后,无论left是否为val,都要往后移动
		// 不是val,往后找是val的位置;是val,被覆盖后,继续往后查找
	}
	return left;
}
/*
int removeElement(vector<int>& nums, int val) {
    int left = 0;
    int right = nums.size() - 1;
    while(left <= right){
        while(left <= right && nums[right] == val){
            right--;  // 找值不为val的位置
        }
        if(left > right){
            break;
        }
        // 如果不是因为越界,那就是因为nums[right] != val
        if(nums[left] == val){
            // 在这里,left指向的一定是val,right指向的一定不是val
            // 所以一定有left < right
            nums[left] = nums[right];
            right--;
        }
        // 所以执行left++前一定不会有left>right
        left++; // nums[left]被覆盖或者是往后寻找值为val的位置
    }
    return left;
}
*/

四、关于普通单链表

1. p->next != nullptrp != nullptr

查找尾结点写法,只有查找尾结点的时候才判断next

Node* p = head;
while(p->next != nullptr){
	p = p->next;
}

遍历链表写法,遍历的时候判断当前结点即可

Node* p = head->next;
while(p != nullptr){
	p = p->next;
}
2. 删除一个值为val的结点
bool remove_one_val(){
	Node* pre = head;
	Node* cur = head->next;
	while(cur != nullptr){
		if(cur->data == val){
			pre->next = cur->next;
			delete cur;
			return true; // 删除
		}else{
			pre = cur;
			cur = cur->next;
		}
	}
	return false; // 没删除
}
3. 删除所有值为val的结点
bool remove_all_val(int val) {
	bool is_del = false;
	Node* pre = head;
	Node* cur = head->next;
	while (cur != nullptr) {
		if (cur->data == val) {
			pre->next = cur->next;
			delete cur;
			is_del = true;
			// 这里pre不可能为空,只有pre->next可能为空
			cur = pre->next;
		}
		else {
			pre = cur;
			cur = cur->next;
		}
	}
	return is_del;
}
4. 销毁整个链表
void destroy(){
	Node* del = head;
	while(nullptr != del){
		head = head->next;
		delete del;
		del = head;
	}
}
5. 不带头结点,删除倒数第n个结点

删除链表的倒数第 N 个结点

ListNode* removeNthFromEnd(ListNode* head, int n) {
   if (n < 1) {
        return head;
    }
    ListNode* pre = head;
    ListNode* cur = head;
    for (int i = 1; i <= n; i++) {
        cur = cur->next;
        if (nullptr == cur) {
        	// 如果cur为nullptr,说明n太大
        	// 然而在leetcode中规定1 <= n <= <结点数目>
        	// 所以只有当 n==结点数目 的时候,cur为nullptr
        	// 删除倒数第<结点数目>个,表示删除第一个结点
            return head->next;
        }
    }
    while (nullptr != cur->next) {
        // cur指向末尾结点时,pre->next即为待删除结点
        cur = cur->next;
        pre = pre->next;
    }
    pre->next = pre->next->next;
    return head;
}
6. 带头结点删除倒数第n个结点
ListNode* removeNthFromEnd(int n) {
	if (n < 1) {
		return head;
	}
	Node* pre = head;
	Node* del = head;
	Node* cur = head;
	for (int i = 1; i <= n; i++) {
		cur = cur->next;
		if (nullptr == cur) {
			return head;
		}
	}
	while (nullptr != cur) {
		cur = cur->next;
		pre = del;
		del = del->next;
	}
	pre->next = del->next;
	delete del;
	return head;
}
7. 不带头结点,合并两个有序链表

合并两个有序链表

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    ListNode* new_head = new ListNode();
    ListNode* last = new_head;
    while(l1 != nullptr && l2 != nullptr){
        if(l1->val > l2->val){
            last->next = l2;
            l2 = l2->next;
            last = last->next;
        }else{
            last->next = l1;
            l1 = l1->next;
            last = last->next;
        }
    }
    last->next = (l1 == nullptr) ? l2 : l1;
    return new_head->next;
}
8. 判环

环形链表

bool hasCycle(ListNode *head) {
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast != nullptr && fast->next != nullptr){
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow){
           return true;
        }
    }
    return false;
}
9. 判环且返回入口地址
ListNode *detectCycle(ListNode *head) {
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast != nullptr && fast->next != nullptr){
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow){
            fast = head;
            while(fast != slow){
                fast = fast->next;
                slow = slow->next;
            }
            return fast;
        }
    }
    return nullptr;
}
10. 判断链表是否相交
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    if(headA == nullptr || headB == nullptr){
        return nullptr;
    }

    ListNode* cur1 = headA;
    ListNode* cur2 = headB;
	
    while(cur1 != cur2){
        cur1 = (cur1 == nullptr) ? headB : cur1->next;
        cur2 = (cur2 == nullptr) ? headA : cur2->next;
    }
    // 就算没有交点,cur1和cur2同时变成nullptr
    return cur1;
}
11. 旋转链表

旋转链表

ListNode* rotateRight(ListNode* head, int k) {
    if(head == nullptr || k == 0){
        return nullptr;
    }
    int len = 1;
    ListNode* tail = head;
    while(tail->next != nullptr){
        len++;
        tail = tail->next;
    }
    tail->next = head;

    // 实际旋转的个数
    k = k % len; 
    // 找到倒数第k+1个结点
    ListNode* cur = head;
    for(int i=1; i<=k; i++){
        cur = cur->next;
    }

    ListNode* new_tail = head;
    // 当cur指向尾结点的时候,new_tail指向需要断开的地方
    while(cur != tail){
        cur = cur->next;
        new_tail = new_tail->next;
    }
    ListNode* head = new_tail->next;
    new_tail->next = nullptr;
    return head;
}

五、单向循环链表

约瑟夫环
// 无头节点,第k个人开始报数,报到m出列,打印出列顺序
void joseph(Node* head, int k, int m) {
	Node* cur = head;
	Node* pre = cur;
	// 使pre的next一直是cur
	while (pre->next != head) {
		pre = pre->next;
	}
	// 从第k个开始报数
	for (int i = 1; i < k; i++) {
		pre = cur;
		cur = cur->next;
	}
	while (true) {
		// 报数
		for (int i = 1; i < m; i++) {
			pre = cur;
			cur = cur->next;
		}
		pre->next = cur->next;
		cout << cur->data << " ";
		delete cur;
		cur = pre->next;
		if (cur == pre) {
			cout << cur->data << " ";
			delete cur;
			cur = nullptr;
			break;
		}
	}
}

六、栈

1. 顺序栈
class SeqStack {
public:
    SeqStack(int size = 10) {
        mstack = new int[size];
        mtop = 0;
        mcap = size;
    }
    ~SeqStack() {
        delete[] mstack;
        mstack = nullptr;
    }
    SeqStack(const SeqStack& src) = delete;
    SeqStack& operator=(const SeqStack& src) = delete;
        
public:
    // push pop top empty 
    void push(int val) {
        if (mtop == mcap) {
            expand();
        }
        mstack[mtop++] = val;
    }

    void pop() {
        if (empty()) {
            throw "stack is empty!";
        }
        else {
            mtop--;
        }
    }

    int top() const {
        if (empty()) {
            throw "stack is empty!";
        }
        return mstack[mtop - 1];
    }

    int size() const{
        return mtop;
    }

    bool empty() const{
        return mtop == 0;
    }
private:
    void expand() {
        int* tmp_stack = new int[2 * mcap];
        memcpy(tmp_stack, mstack, sizeof(int) * mtop);
        delete mstack;
        mstack = tmp_stack;
        mcap *= 2;
    }
private:
    int* mstack;
    int mtop;    // 栈内元素
    int mcap;    // 栈容量,开辟的堆内存大小
};
2. 链栈
class LinkStack {
public:
    LinkStack() {
        head = new Node();
        head->data = 0;    // 用头节点的数据域存放数据节点的数量
    }
    ~LinkStack() {
        while (head != nullptr) {
            Node* del = head;
            head = head->next;
            delete del;
        }
    }
    LinkStack(const LinkStack& src) = delete;
    LinkStack& operator=(const LinkStack& src) = delete;

public:
    // push pop top empty 
    void push(int val) {
        // 采用头插法
        Node* node = new Node(val);
        node->next = head->next;
        head->next = node;
        head->data++;
    }

    void pop() {
        if (empty()) {
            throw "stack is empty!";
        }
        Node* del = head->next;
        head->next = del->next;
        delete del;
        head->data--;
    }

    int top() const {
        if (empty()) {
            throw "stack is empty!";
        }
        return head->next->data;
    }

    int size() const {
        return head->data;
    }

    bool empty() const {
        return head->data == 0;
    }

private:
    struct Node {
        Node(int d = 0)
            :data(d)
            ,next(nullptr) {

        }
        int data;
        Node* next;
    };
    Node* head;
};
3. 括号匹配

有效的括号

class Solution {
public:
    bool is_match(char a, char b) {
        if ((a == '(' && b == ')')) {
            return true;
        }
        else if ((a == '[' && b == ']')) {
            return true;
        }
        else if ((a == '{' && b == '}')) {
            return true;
        }
        return false;
    }

    bool isValid(string s) {
        stack<char> st;
        for (char c : s) {
            if (c == '(' || c == '[' || c == '{') {
                st.push(c);
            }
            else {
                // 右括号
                if (!st.empty() && is_match(st.top(), c)) {
                    st.pop();
                }
                else {
                    return false;
                }
            }
        }
        return st.empty();
    }
};
4. 逆波兰表达式

求解逆波兰表达式

  1. 数字则入栈
  2. 运算符出栈两个数字,先出的是右操作数,后出的是左操作数
  3. 两个数字的计算结果入栈
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(string s : tokens){
            if(s == "+" || s == "-" || s == "*" || s == "/"){
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                switch (s[0]) {
                    case '+':
                        st.push(left + right);
                        break;
                    case '-':
                        st.push(left - right);
                        break;
                    case '*':
                        st.push(left * right);
                        break;
                    case '/':
                        st.push(left / right);
                        break;
                    }
            }else{
                st.push(stoi(s)); // atoi(s.c_str())
            }
        }
        return st.top(); // 最后的计算结果存放在栈中
    }
};
5. 中缀转后缀表达式
  1. 栈空,符号直接入栈
  2. 符号为,直接入栈
  3. 符号为,一直出栈,直到出栈一个
  4. 用当前符号和栈顶符号比较优先级
    (1) 当前符号优先级 > 栈顶符号优先级:当前符号入栈
    (2) 当前符号优先级 <= 栈顶符号优先级:栈内符号出栈,继续和当前符号比较,直到符号出完。
// cur优先级是否大于stack_c
bool is_high_priority(char cur, char stack_c) {
	if (stack_c == '(') {
		return true;
	}
	return (cur == '*' || cur == '/') && (stack_c == '+' || stack_c == '-');
}

// 四则运算和小括号的表达式
string MiddleToEndExpr(string str) {
	stack<char> st;
	string res;
	for (int i = 0; i < str.size(); i++) {
		if (st.empty() || str[i] == '(') {
			st.push(str[i]);
		}
		else if (str[i] == ')') {
			while (st.top() != '(') {
				// 表达式正确的话,这里栈不可能为空
				// 因为只要遇见了右括号,栈内一定有一个左括号
				res += st.top();
				st.pop();
			}
			st.pop(); // 出栈左括号
		}
		else {
			// 数字字符
			if (str[i] >= '0' && str[i] <= '9') {
				res += str[i];
			}
			else {
				// 运算符
				if (is_high_priority(str[i], st.top())) {
					// 当前字符优先级大于栈顶字符优先级
					st.push(str[i]);
				}
				else {
					res += st.top();
					st.pop();
					i--;
				}
			}
		}
	}
	// 剩下字符全部出栈
	while (!st.empty()) {
		res += st.top();
		st.pop();
	}
	return res;
}

七、队列

1. 顺序环形队列常用公式
// front指向第一个有效元素  rear指向最后一个有效元素的后继位置
front == rear  // 判空
(rear + 1) % len == front  // 判满
(rear - front + len) % len  // 元素个数
2. 顺序环形队列
class Queue {
public:
	Queue(int size = 10) {
		_q = new int[size];
		_cap = size;
		_front = 0;
		_rear = 0;
	}
	~Queue() {
		delete _q;
		_q = nullptr;
	}
public:
	bool empty() const{
		return _front == _rear;
	}

	bool full() const {
		return (_rear + 1) % _cap == _front;
	}

	int size() const{
		return (_rear - _front + _cap) % _cap;
	}

	void push(int val) {
		if (full()) {
			expand();
		}
		_q[_rear] = val;
		_rear = (_rear + 1) % _cap;
	}

	void pop() {
		if (empty()) {
			throw "queue is empty!";
		}
		_front = (_front + 1) % _cap;
	}

	int front() const{
		if (empty()) {
			throw "queue is empty!";
		}
		return _q[_front];
	}

	int back() const{
		return _q[(_rear - 1 + _cap) % _cap];
	}

private:
	void expand() {
		const int time = 2;
		int* tmp_q = new int[time * _cap];
		int dst = 0;
		for (int src = _front; src != _rear; src = (src + 1) % _cap) {
			tmp_q[dst] = _q[src];
			dst++;
		}
		delete[] _q;
		_q = tmp_q;
		_cap *= time;
		_front = 0;
		_rear = dst;
	}

private:
	int* _q;
	int _cap;
	int _front;
	int _rear;
};
3. 链式队列
class LinkQueue {
public:
	LinkQueue() {
		_head = new Node();
		_head->_pre = _head;
		_head->_next = _head;
		_size = 0;
	}

	~LinkQueue() {
		Node* del = _head->_next;
		while (del != _head) {
			_head->_next = del->_next;
			del->_next->_pre = _head;
			delete del;
			del = _head->_next;
		}
		delete _head;
		_head = nullptr;
	}
public:
	bool empty() const {
		return _head->_next == _head;
	}

	int size() const {
		return _size;
	}

	void push(int val) {
		Node* node = new Node(val);
		node->_pre = _head->_pre;
		node->_next = _head;
		_head->_pre->_next = node;
		_head->_pre = node;
	}

	void pop() {
		if (empty()) {
			throw "queue is empty!";
		}
		Node* del = _head->_next;
		_head->_next = del->_next;
		del->_next->_pre = _head;
		delete del;
	}

	int front() const {
		if (empty()) {
			throw "queue is empty!";
		}
		return _head->_next->_data;
	}

	int back() const {
		if (empty()) {
			throw "queue is empty!";
		}
		return _head->_pre->_data;
	}

private:
	struct Node {
		Node(int data = 0)
			:_data(data)
			,_pre(nullptr)
			,_next(nullptr)
		{}
		int _data;
		Node* _pre;
		Node* _next;
	};
	Node* _head;
	int _size;
};
4. 两个栈实现一个队列

用栈实现队列

class MyQueue {
public:
    MyQueue() {

    }
    
    void push(int x) {
        in_st.push(x);
    }
    
    int pop() {
        if(out_st.empty()){
            while(!in_st.empty()){
                out_st.push(in_st.top());
                in_st.pop();
            }
        }
        int val = out_st.top();
        out_st.pop();
        return val;
    }
    
    int peek() {
        if(out_st.empty()){
            while(!in_st.empty()){
                out_st.push(in_st.top());
                in_st.pop();
            }
        }
        return out_st.top();
    }
    
    bool empty() {
        return in_st.empty() && out_st.empty();
    }

private:
    stack<int> in_st;
    stack<int> out_st;
};
5. 两个队列实现一个栈

用队列实现栈

每次入栈时都将元素入到空队列A,然后把另一个队列B的所有元素都导入当前这个队列A,这样当前队列A的元素出队列的顺序就和栈一样了,下次入栈再把元素入到空队列。

每次进行push,都是入到空队列,然后把不空队列的所有元素导入有新元素加入的队列

class MyStack {
public:
    MyStack() {
        empty_q = new queue<int>();
        fine_q = new queue<int>();
    }
    
    // O(n)
    void push(int x) {
        empty_q->push(x);
        while(!fine_q->empty()){
            empty_q->push(fine_q->front());
            fine_q->pop();
        }
        queue<int> *tmp = empty_q;
        empty_q = fine_q;
        fine_q = tmp;
    }
    
    int pop() {
        int val = fine_q->front();
        fine_q->pop();
        return val;
    }
    
    int top() {
        return fine_q->front();
    }
    
    bool empty() {
        return fine_q->empty();
    }

private:
    queue<int> *empty_q; // 每次入队,都入这个空队列
    queue<int> *fine_q;  // 已经调整好栈顺序的队列
};
6. 用一个队列实现一个栈

每次入一个元素,需要把其他元素全部都出队并入到新元素的后面,每次push都是O(n)

class MyStack {
public:
    MyStack() {

    }
    
    // O(n)
    void push(int x) {
        q.push(x);
        int len = q.size() - 1;
        for(int i = 0; i < len; i++){
            q.push(q.front());
            q.pop();
        }
    }
    
    int pop() {
        int val = q.front();
        q.pop();
        return val;
    }
    
    int top() {
        return q.front();
    }
    
    bool empty() {
        return q.empty();
    }

private:
    queue<int> q;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值