算法总结归纳(第二天)(数据结构线性表总结:链表、栈与队列、单调栈、单调队列、字符串)

目录

一、链表

Ⅰ、使用指针类型表示链表

1、移除链表元素

①、使用原链表进行操作

②、建立虚拟头节点操作

2、设计链表(熟悉链表的基本操作)

3、双指针操作链表类型题目

①、反转链表

②、两两交换链表中的元素

③、删除倒数第n个节点

Ⅱ、使用数组类型表示链表

1、单链表

2、双链表

二、栈与队列

Ⅰ、普通栈

1.使用数组实现栈

(重要)2、表达式求值(逆波兰表达式)

Ⅱ、队列

1、使用数组实现队列

Ⅲ、单调栈

Ⅳ、单调队列(滑动窗口)

三、字符串

Ⅰ、反转字符串系列

①、简单反转

1.反转Ⅰ

2、反转Ⅱ

②、复杂反转

1、反转单词


一、链表

Ⅰ、使用指针类型表示链表

1、移除链表元素

题目链接:

移除链表元素

①、使用原链表进行操作

思路看下图注释

 while(head != NULL && head ->val == val){
           struct ListNode* tem = head;
           head = head->next;
           delete tem;
       }
//分两部分,先去除头部可能有val的部分,然后去除后面的部分
//通常去除后面的部分通常会用一个新的指针去遍历去除。

       struct ListNode* cur = head;
       while(cur != NULL && cur ->next != NULL)
       {
           if(cur ->next ->val == val){
               cur ->next = cur->next->next;
           }
           else cur = cur->next;
       }
       return head;

②、建立虚拟头节点操作

该方法将前面的思路直接合并。

 struct ListNode* nhead = (struct ListNode*) malloc(sizeof(struct ListNode));
       nhead ->next = head;
       struct ListNode* tem = nhead;
       while(tem != NULL && tem ->next!=NULL)
       {
           if(tem ->next->val == val){
               tem ->next = tem ->next ->next;
           }
           else tem = tem ->next;
       }
       head = nhead->next;
       return head;
//该方法较上面的好处是可以将移除头部元素和尾部元素的情况合并,一次遍历成功。

2、设计链表(熟悉链表的基本操作)

题目链接:

设计链表

该题目较为全面的实现了基本插入、删除、查找、清空操作,用来熟悉链表非常好用。



//定义结构体
typedef struct  MyLinkedList{
    int val;
     struct MyLinkedList* next;
} MyLinkedList;

//建立头节点
MyLinkedList* myLinkedListCreate() {
     MyLinkedList* head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
     head->next = NULL;
     return head;
}

//获取链表中元素
int myLinkedListGet(MyLinkedList* obj, int index) {
    MyLinkedList *cur = obj->next;
    for (int i = 0; cur != NULL; i++){
        if (i == index){
            return cur->val;
        }
        else{
            cur = cur->next;
        }
    }
    return -1;
}

//头插法
void myLinkedListAddAtHead(MyLinkedList* head, int val) {
    MyLinkedList* addhead = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    addhead->val = val;addhead->next = head->next;
    head->next = addhead;
}

//尾部插法
void myLinkedListAddAtTail(MyLinkedList* head, int val) {
    MyLinkedList* tem = head;
    while(tem->next!=NULL){
        tem = tem->next;
    }
    MyLinkedList* cur = (MyLinkedList*)malloc(sizeof(MyLinkedList));
    cur->val = val;cur->next = NULL;
    tem->next = cur;
}

//中间插入法
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
    if (index == 0){
        myLinkedListAddAtHead(obj, val);
        return;
    }
    MyLinkedList *cur = obj->next;
    for (int i = 1 ;cur != NULL; i++){
        if (i == index){
            MyLinkedList* newnode = (MyLinkedList *)malloc(sizeof (MyLinkedList));
            newnode->val = val;
            newnode->next = cur->next;
            cur->next = newnode;
            return;
        }
        else{
            cur = cur->next;
        }
    }
}

//删除链表元素
void myLinkedListDeleteAtIndex(MyLinkedList* head, int index) {
   if(index==0) {
       MyLinkedList* tem = head->next;
       if(tem!=NULL){
           head->next = tem->next;
           free(tem);
       }
   }
   MyLinkedList* cur = head->next;
   for(int i = 1;cur!=NULL;i++){
       if(i==index){
           MyLinkedList* temp = cur->next;
           if(temp!=NULL){
               cur->next = temp->next;
               free(temp);
           }
           return;
       }
       else{
           cur = cur->next;
       }
   }
}

//清空元素
void myLinkedListFree(MyLinkedList* head) {
   while(head){
       MyLinkedList* p2 = head;
       head = head->next;
       free(p2);
   }
}

/**
 * Your MyLinkedList struct will be instantiated and called as such:
 * MyLinkedList* obj = myLinkedListCreate();
 * int param_1 = myLinkedListGet(obj, index);
 
 * myLinkedListAddAtHead(obj, val);
 
 * myLinkedListAddAtTail(obj, val);
 
 * myLinkedListAddAtIndex(obj, index, val);
 
 * myLinkedListDeleteAtIndex(obj, index);
 
 * myLinkedListFree(obj);
*/

3、双指针操作链表类型题目

①、反转链表

题目链接:反转链表

下面表格表示一次的变化情况:

NULL(pre)   ->  node1 (cur)   -> node2    -> NULL
NULl(pre) <-   node1(cur) node2(temp)   -> NULL
NULL <-   node1(pre) node2(temp)  -> NULL
NULL <-   node1(pre node2(cur)    ->  NULL
struct ListNode* cur = head; struct ListNode* pre = NULL;
    while(cur != NULL){
        struct ListNode* temp = cur ->next;
        cur ->next = pre;
        pre = cur;
        cur = temp;
    }
    return pre;
②、两两交换链表中的元素

题目链接:

交换链表元素

此处逻辑需要画图(按照下面代码一画图便理解了)来理清关系,此处while中的代码为重复的业务逻辑。

nhead(cur) ->

虚拟头节点

node1(pre)  ->

节点1

node2   ->

节点2

NULL
 if(head == NULL || head ->next == NULL) return head;
    struct ListNode* nhead = (struct ListNode*) malloc(sizeof(struct ListNode));
    nhead ->next = head;
    struct ListNode* cur = nhead; struct ListNode* pre = cur ->next;
    while(cur != NULL && pre != NULL && pre ->next != NULL)
    {
        cur ->next = cur ->next ->next;
        pre ->next = cur ->next ->next;
        cur ->next ->next = pre;
        cur = pre;
        pre = pre ->next;
    }
    return nhead ->next;
③、删除倒数第n个节点

题目链接:

删除倒数第n个节点

struct ListNode* nhead = (struct ListNode*)malloc(sizeof(struct ListNode));
    nhead->next = head;
    if(nhead->next == NULL) return NULL;
    struct ListNode* slow = nhead;
    struct ListNode* fast = nhead->next;
    int i = 1;
    for(i = 1;i<=n;i++){
        if(fast!=NULL) {
            fast = fast->next;
        }
    }
    while(fast!=NULL){
        fast = fast->next;
        slow = slow->next;
    }
    struct ListNode* tem = slow->next;
    slow->next = tem->next;
    free(tem);
    return (nhead->next);

Ⅱ、使用数组类型表示链表

1、单链表

题目链接:单链表(数组实现)

该代码的关键在于ne[]数组的理解,以及idx作用的理解。

①、ne[]数组可以理解为链表节点的指针域。而idx可以理解为代替指针的一种东西,每个idx的数值不一样,例如ne[k] = k+1的意义就是,k这个下标的数组指向 下标为 k + 1的数组。

②、我们每次将idx++。就是为了保证idx对每个节点的唯一性。

#include
using namespace std;
int head,idx;
const int N = 1e5+10;
int e[N],ne[N];

void add_to_head(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx++;
}

void remove(int k)
{
    ne[k] = ne[ne[k]];
}

void add(int k,int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}

int main()
{
    head = -1;idx = 0;
    int n;cin>>n;
    for(int i = 0;i>s;
        if(s == 'H')
        {
            int x;cin>>x;
            add_to_head(x);
        }
        if(s == 'I')
        {
            int k,x;cin>>k>>x;
            add(k-1,x);
        }
        if(s == 'D')
        {
            int k;cin>>k;
            if(!k) head = ne[head];
            remove(k-1);
        }
    }
    for(int i = head;i!=-1;i = ne[i]) cout<

2、双链表

本题目则是使用l[]和r[]数组来表示双链表的左右两端。

从而实现双链表功能。idx依然起代替指针的作用。

#include

using namespace std;

const int N = 1e5 + 10;

int m;
int e[N], l[N], r[N];
int idx;


//! 初始化
void init()
{
    l[1] = 0, r[0] = 1;
    idx = 2;
}

void add(int k, int x)
{
    e[idx] = x;
    l[idx] = k;
    r[idx] = r[k];
    l[r[k]] = idx;
    r[k] = idx;
    idx++;
}

void remove(int k)
{
    l[r[k]] = l[k];
    r[l[k]] = r[k];
}

int main()
{
    cin >> m;

    init();

    while(m--)
    {
        string op;
        cin >> op;
        int k, x;
        if(op=="R")
        {
            cin >> x;
            add(l[1], x); 
        }
        else if(op=="L")
        {
            cin >> x;
            add(0, x);
        }
        else if(op=="D")
        {
            cin >> k;
            remove(k + 1);
        }
        else if(op=="IL")
        {
            cin >> k >> x;
            add(l[k + 1], x);
        }
        else
        {
            cin >> k >> x;
            add(k + 1, x);
        }    
    }
    for(int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';

    return 0;
}

二、栈与队列

Ⅰ、普通栈

1.使用数组实现栈

题目链接:

模拟栈

栈的结构非常简单,使用数组很简单就能实现。

#include
using namespace std;
const int N = 1e5 + 10;
int stack[N];
int top = -1, n;

int main()
{
    cin >> n;
    while(n--)
    {
        string s;
        cin >> s;

        //栈顶所在索引往后移动一格,然后放入x。
        if(s == "push")
        {
            int a; cin>>a;
            stack[++top] = a;
        }

        //往前移动一格
        if(s == "pop")
        {
            top--;
        }
        //返回栈顶元素
        if(s == "query")
        {
            cout<= 0) cout<<"NO"<

(重要)2、表达式求值(逆波兰表达式)

题目链接:

表达式求值

unordered_map

h{{'+',1},{'-',1},{'*',2},{'/',2}};

此处运用map将h中运算符的优先级进行定义,数字大的优先级大。

op[]用来存放非数字的数据,num[]用来存放数字。

#include
using namespace std;
#include 
#include
#include

stack num;
stack op;

unordered_map h{{'+',1},{'-',1},{'*',2},{'/',2}};

void eval()
{
    int a = num.top();num.pop();
    int b= num.top();num.pop();
    char r = op.top();op.pop();
    int x = 0;
    if(r=='+') x = b+a;
    else if(r=='-') x = b-a;
    else if(r == '*') x = b*a;
    else if(r=='/') x = b/a;
    num.push(x);
}

int main()
{
    string s;cin>>s;
   for (int i = 0; i < s.size(); i++)
    {
        if (isdigit(s[i]))//数字入栈
        {
            int x = 0, j = i;//计算数字
            while (j < s.size() && isdigit(s[j]))
            {
                x = x * 10 + s[j] - '0';
                j++;
            }
            num.push(x);//数字入栈
            i = j - 1;
        }
        //左括号无优先级,直接入栈
        else if (s[i] == '(')//左括号入栈
        {
            op.push(s[i]);
        }
        //括号特殊,遇到左括号直接入栈,遇到右括号计算括号里面的
        else if (s[i] == ')')//右括号
        {
            while(op.top() != '(')//一直计算到左括号
                eval();
            op.pop();//左括号出栈
        }
        else
        {
            while (op.size() && h[op.top()] >= h[s[i]])//待入栈运算符优先级低,则先计算
                eval();
            op.push(s[i]);//操作符入栈
        }
    }
    while (op.size()) eval();//剩余的进行计算
    cout << num.top() << endl;//输出结果
    return 0;
}

Ⅱ、队列

1、使用数组实现队列

题目链接:

模拟队列

#include
using namespace std;
const int N = 100010;
int q[N];

int hh = 0;//队头
int tt = -1;//队尾
int m;
string s;

void push(int x){
   q[++tt] = x;
   
}

void pop(){
   hh++; 
}

void empty(){
    if(tt >= hh) cout << "NO" << endl;
    else cout << "YES" << endl;
} 

void query (){
    cout << q[hh] << endl;
}

int main(){
    cin >> m;
    while(m--){
        cin >> s;
        //入队
        if(s == "push"){
            int x;
            cin >> x;
            push(x);
        }
        //出队
        if(s == "pop"){
            pop();
        }
        //问空
        if(s == "empty"){
            empty();
        }
        //问队头
        if(s == "query"){
            query();
        }
    }
}

Ⅲ、单调栈

题目链接:

数组实现单调栈

#include
using namespace std;
const int N = 1e5+10;
int tt,skt[N];

int main()
{
    int n; cin>>n;
    for(int i = 0; i>x;
        while(tt != 0 && skt[tt] >= x) tt--;
        if(tt != 0) cout<

Ⅳ、单调队列(滑动窗口)

题目链接:

滑动窗口

#include 
#include 
using namespace std;

const int N = 1000010;
int a[N];
int main()
{
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];//读入数据
    deque q;
    for(int i = 1; i <= n; i++)
    {
       while(q.size() != 0 && q.back() > a[i]){
           q.pop_back();
       }
       q.push_back(a[i]);
       if(i - k >= 1 && q.front() == a[i - k]){
           q.pop_front();
       }
       if(i >= k){
           cout<= 1 && q.front() == a[i - k]){
            q.pop_front();
        }
        if(i >= k){
         cout<

三、字符串

Ⅰ、反转字符串系列

①、简单反转

1.反转Ⅰ

题目链接:

反转字符串Ⅰ

void reverseString(vector& s) {
        int i, j;
        i = 0; j = s.size() - 1;
        while(i < j)
        {
            swap(s[i ++], s[j --]);
        }
    }
2、反转Ⅱ

题目链接:

反转字符串Ⅱ

 for(int i = 0;i

②、复杂反转

1、反转单词

题目链接:

反转字符串中的单词

 void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
        int slow = 0;   
        for (int i = 0; i < s.size(); ++i) { //
            if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
                if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。
    }

    string reverseWords(string s) {
        removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s, 0, s.size() - 1);
        int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。
        for (int i = 0; i <= s.size(); ++i) {
            if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。
                reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。
                start = i + 1; //更新下一个单词的开始下标start
            }
        }
        return s;

你可能感兴趣的:(算法,数据结构,c++)