数据结构和算法学习

数据结构

数据结构是计算机内部数据的组织形式和存储方法,常用的数据结构有线性结构、数结构、图结构。

线性结构

线性结构主要包括:顺序表、链表、栈、队列等基本形式。其中顺序表和链表是从存储形式上(或物理结构上)区分的,而栈和队列是从逻辑功能上区分的。也就是说,顺序表和链表是线性数据结构的基础,队列和栈是基于顺序表和链表的,它们由顺序表或链表构成。

栈的实现
#ifndef STACK_HPP#define STACK_HPP#include <iostream>using namespace std;template <class T>class Stack {public:    Stack();    Stack(int capacity);    ~Stack();    bool IsEmpty();                 // 判断是否为空    bool IsFull();                  // 判断栈是否已满    void ClearStack();              // 清空栈    int Length();                   // 获取栈的长度    bool Push(const T &element);    // 获取元素    bool Pop(T &ele);               // 弹出栈顶元素    void StackTraverse();           // 遍历栈private:    T *m_stack;     // 容器    int m_top;      // 栈顶    int m_capacity; // 容器容量};template <typename T>Stack<T>::Stack() {    cout << "构造函数" << endl;}template <typename T>Stack<T>::Stack(int capacity) {    this->m_capacity = capacity;    this->m_top = 0;    this->m_stack = new T[this->m_capacity];}template <typename T>Stack<T>::~Stack() {    if (this->m_stack != NULL) {        delete[] this->m_stack;        this->m_stack = NULL;    }}template <typename T>bool Stack<T>::IsEmpty() {    return this->m_top == 0 ? true : false;}template <typename T>bool Stack<T>::IsFull() {    return this->m_top == this->m_capacity ? true : false;}template <typename T>void Stack<T>::ClearStack() {    this->m_top = 0;}template <typename T>int Stack<T>::Length() {    return this->m_top;}template <typename T>bool Stack<T>::Push(const T &element) {    if (IsFull()) {        return false;    } else {        this->m_stack[this->m_top++] = element;        return true;    }}template <typename T>bool Stack<T>::Pop(T &ele) {    if (!IsEmpty()) {        this->m_top--;        ele = this->m_stack[this->m_top];        return true;    } else {        return false;    }}template <typename T>void Stack<T>::StackTraverse() {    if (!IsEmpty()) {        for (int i = m_top - 1; i >= 0; --i) {            cout << this->m_stack[i] << ' ';        }        cout << endl;    } else {        cout << "栈为空" << endl;    }}#endif
队列的实现
#ifndef MYQUEUE_HPP#define MYQUEUE_HPP#include <iostream>using namespace std;template <typename T>struct QueueNode {    T data;    QueueNode *next;    QueueNode () {        this->next = NULL;    }};template <class T>class MyQueue {public:    MyQueue() {        this->count = 0;        this->head = new QueueNode <T>();        this->tail = new QueueNode <T>();        this->tail = this->head;    }    ~MyQueue() {        if (this->head != NULL) {            cout << "释放head" << endl;            delete[] this->head;            this->head = NULL;        }        if (this->tail != NULL) {            cout << "释放tail" << endl;            delete[] this->tail;            this->tail = NULL;        }    }    int size() {        return this->count;    }    bool IsEmpty() {        if (size() == 0) {            return true;        } else {            return false;        }    }    void InQueue(T val) {        QueueNode <T> *temp = new QueueNode<T>();        temp->data = val;        this->tail->next = temp;        this->tail = temp;        this->count++;    }    QueueNode<T>* OutQueue() {        if (this->count == 0) {            cout << "队列为空" << endl;            return NULL;        }        QueueNode <T> *temp = this->head->next;        if (temp) {            this->head->next = temp->next;            temp->next = NULL;            this->count--;        }        return temp;    }    void QueueTraverse() {        if (this->count == 0) {            cout << "队列为空" << endl;            return;        }        QueueNode <T> *temp = head->next;        while (temp) {            cout << temp->data << ' ';            temp = temp->next;        }        cout << endl;    }    void Remove() {        this->head->next = NULL;        this->tail = this->head;        this->count = 0;    }private:    int count;    QueueNode <T> *head;    QueueNode <T> *tail;};#endif
链表反转

使用两个节点pre和next,先用next保存当前节点的next指针,然后将当前节点next指针指向上一个节点pre,接下来将当前节点赋值给pre,next赋值给当前节点进行下一循环,直到当前节点为NULL。

struct ListNode {	int val;	struct ListNode *next;	ListNode(int x) : val(x), next(NULL) {}};/** * 链表反转 */ListNode* ReverseList(ListNode *pHead) {    if(pHead == NULL)        return NULL;    ListNode *pre = NULL;    ListNode *next = NULL;    while (pHead != NULL) {        next = pHead->next;        pHead->next = pre;        pre = pHead;        pHead = next;    }    return pre;}
寻找第k大
int Partition(vector<int> &arr, int start, int end) {    // 降序排列    if (start == end)        return start;    int left = start, right = end, key = arr[start];    while (left < right) {        while (left < right && arr[right] <= key)            right--;        arr[left] = arr[right];        while (left < right && arr[left] >= key)            left++;        arr[right] = arr[left];    }    arr[left] = key;    return left;}/** * 寻找第k大 */int findKth(vector<int> a, int n, int K) {    int low = 0;    int high = n - 1;    int mid;    while (low <= high) {        mid = Partition(a, low, high);        if (K < mid + 1) {            // 在左半段            high = mid - 1;        } else  if (K > mid + 1) {            // 在右半段            low = mid + 1;        } else {            return a[mid];        }    }    return -1;}
括号序列

给出一个仅包含字符’(‘,’)’,’{‘,’}’,’[‘和’]’,的字符串,判断给出的字符串是否是合法的括号序列,括号必须以正确的顺序关闭,”()”和”()[]{}”都是合法的括号序列,但”(]”和”([)]”不合法。

bool isValid(string s) {    // 如果字符串长度不是偶数,则不合法    if (s.size() & 1 != 0)        return false;    stack<char> temp;    for (int i = 0; i < s.size(); ++i) {        if (s[i] == '(' || s[i] == '{' || s[i] == '[')            temp.push(s[i]);        else {            if (temp.empty())                return false;            switch (s[i]) {                case '}':                    if (temp.top() == '{')                        temp.pop();                    break;                case ']':                    if (temp.top() == '[')                        temp.pop();                    break;                case ')':                    if (temp.top() == '(')                        temp.pop();                    break;                default:                    return false;            }        }    }    if (!temp.empty())        return false;    return true;}
链表中的节点每k有一组翻转

题目描述

将给出的链表中的节点每$k$个一组翻转,返回翻转后的链表
如果链表中的节点数不是$k$的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。
要求空间复杂度$O(1)$。
例如:
给定的链表是$1→2→3→4→5$

  • 对于 $k = 2$, 你应该返回 $2→1→4→3→5$
  • 对于 $k = 3$, 你应该返回 $3→2→1→4→5$
/** * 链表中的节点每k有一组翻转 * @param head * @param k * @return */ListNode* reverseKGroup(ListNode* head, int k) {    if (head == NULL)        return NULL;    ListNode *temp = head;    int len = 0;    while (temp != NULL) {        temp = temp->next;        ++len;    }    temp = head;    if (len < k)        return head;    ListNode *pre = NULL;    ListNode *next = NULL;    for (int i = 0; i < k; ++i) {        next = head->next;        head->next = pre;        pre = head;        head = next;    }    temp->next = reverseKGroup(head, k);    return pre;    }
判断链表中是否有环

链表和快慢指针

/** * 判断给定的链表是否有环 * @param head * @return */bool hasCycle(ListNode *head) {    ListNode *low, *quick;    low = quick = head;    while (quick->next->next != NULL && quick->next != NULL) {        low = low->next;        quick = quick->next->next;        if (low == quick)            return true;    }    return false;}
链表中环的入口
ListNode *detectCycle(ListNode *head) {    if (head == NULL)        return 0;    ListNode *slow = head;    ListNode *fast = head;    while (fast != NULL && fast->next != NULL) {        slow = slow->next;        fast = fast->next->next;        if (slow == fast)            break;    }    if (fast == NULL || fast->next == NULL)        return NULL;    fast = head;    while (slow != fast) {        slow = slow->next;        fast = fast->next;    }    return slow;}
合并有序链表
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {    ListNode *head = new ListNode(-1);    ListNode *temp = head;    while (l1 != NULL || l2 != NULL) {        if (l1 == NULL) {            temp->next = l2;            break;        }        if (l2 == NULL) {            temp->next = l1;            break;        }        if (l1->val < l2->val) {            temp->next = l1;            l1 = l1->next;        } else {            temp->next = l2;            l2 = l2->next;        }        temp = temp->next;    }    return head;}
删除链表的倒数第N个节点
/** * 删除链表倒数第N个节点 * @param head ListNode类  * @param n int整型  * @return ListNode类 */ListNode* removeNthFromEnd(ListNode* head, int n) {    if (head == NULL)        return NULL;    ListNode *dummy = new ListNode(0);    dummy->next = head;    head = dummy;    ListNode *slow = head;    ListNode *fast = head;    for (int i = 0; i < n; ++i) {        fast = fast->next;    }    while (fast->next != NULL) {        slow = slow->next;        fast = fast->next;    }    ListNode *temp = slow->next;    slow->next = slow->next->next;    delete temp;    return dummy->next;}
两个链表相加生成相加链表
/** * 两个链表相加生成相加链表 */ListNode* addInList(ListNode* head1, ListNode* head2) {    stack<ListNode *> s1;    stack<ListNode *> s2;    while (head1) {        s1.push(head1);        head1 = head1->next;    }    while (head2) {        s2.push(head2);        head2 = head2->next;    }    int flag = 0;    while (!s1.empty() || !s2.empty()) {        int sum = flag;        if (!s1.empty()) {            sum += s1.top()->val;            head1 = s1.top();            s1.pop();        }        if (!s2.empty()) {            sum += s2.top()->val;            if (s2.size() > s1.size())                head1 = s2.top();            s2.pop();        }        if (sum < 10) {            head1->val = sum;            flag = 0;        } else {            head1->val = sum % 10;            flag = sum / 10;        }    }    if (flag > 0) {        ListNode *head = new ListNode(flag);        head->next = head1;        return head;    }    return head1;}

树结构

数据之间存在“一对多”的关系构成了树结构。

图结构

图结构中数据元素之间存在着“多对多”的关系。

算法

查找和排序

顺序查找
int search(Student *stu, int n, int  key) {    for (int i = 0; i < n; ++i) {        if (stu[i].id == key)            return i;    }    return -1;}
折半查找
/** * 折半查找 * @param key 关键字顺序表 * @param n 记录个数 * @param k 要查找的关键字 * @return */int bin_search(int *key, int n, int k) {    int low = 0, high = n - 1, mid;    while (low <= high) {        mid = (low + high) / 2;        if (key[mid] == k)            return mid;        if (k > key[mid])            low = mid + 1;        else             high = mid - 1;    }    return -1;}

请实现有重复数字的有序数组的二分查找。
输出在数组中第一个大于等于查找值的位置,如果数组中不存在这样的数,则输出数组长度加一。

int bin_search(int n, int v, vector<int>& a) {    int left = 0;    int right = n - 1;    int mid;    while (left <= right) {        mid = (left + right) / 2;        if (a[mid] >= v) {            if (mid == 0 || a[mid-1] < v)                return mid + 1;            else                right = mid - 1;        }        else            left = mid + 1;    }    return n + 1;}
直接插入排序
/** * 直接插入排序 * @param arr 数组 * @param n 数组元素个数 */void insertSort(int arr[], int n) {    for (int i = 1; i < n; ++i) {        int temp = arr[i];        int j = i - 1;        // for (j; j >= 0; --j) {        //     if (temp < arr[j]) {        //         arr[j + 1] = arr[j];        //     } else {        //         break;        //     }        // }        // arr[j + 1] = temp;        while (j >= 0 && temp < arr[j]) {            arr[j+1] = arr[j];            j--;        }        arr[j+1] = temp;    }}
选择排序
/** * 选择排序 * @param arr 数组 * @param n 数组元素个数 */void selectSort(int arr[], int n) {    for (int i = 0; i < n; ++i) {        int min = i, temp;        for (int j = i; j < n; ++j) {            if (arr[j] < arr[min])                min = j;        }        if (min != i) {            temp = arr[min];            arr[min] = arr[i];            arr[i] = temp;        }    }}
冒泡排序
/** * 冒泡排序 * 添加标志变量flag,进行交换flag为真,可以进入下一循环,为进行交换flag为假(数据已有序,无须再进行交换)循环终止。 * @param arr 数组 * @param n 数组元素个数 */void BubbleSort(int arr[], int n) {    bool flag = true;    for (int i = 0; i < n && flag; ++i) {        flag = false;        for (int j = 0; j < n - i - 1; ++j) {            if (arr[j] > arr[j + 1]) {                flag = true;                int temp = arr[j];                arr[j] = arr[j + 1];                arr[j + 1] = temp;            }        }    }}
希尔排序
/** * 希尔排序 * 使用do-while是因为flag为2时已经有序,但是flag为1时不是有序的。 * @param arr 数组 * @param n 数组元素个数 */void shellSort(int arr[], int n) {    int i, j, gap = n;    bool flag = true;    int temp;    while(gap > 1) {        gap = gap / 2;        do {            flag = false;            for (i = 0; i < n - gap; ++i) {                j = i + gap;                if (arr[i] > arr[j]) {                    temp = arr[i];                    arr[i] = arr[j];                    arr[j] = temp;                    flag = true;                }            }        } while(flag);    }}
快速排序
void swap(int &a, int &b) {    int temp = a;    a = b;    b = temp;}void quick(int arr[], int start, int end) {    if (start >= end)        return;    int left = start, right = end, key = arr[start];    // 进行循环,将小于基准元素的元素放到左边,大于基准元素的元素放到右边    while (left < right) {        while(left < right && arr[right] >= key)            right--;        while(left < right && arr[left] <= key)            left++;        swap(arr[left], arr[right]);        // for (int i = 0; i < 10; ++i) {        //     cout << arr[i] << ' ';        // }        // cout << endl;    }    // 交换基准元素与left的位置,循环中先从右开始,因此left和right相等的位置为小于基准元素的值    swap(arr[start], arr[left]);    quick(arr, start, left - 1);    quick(arr, left + 1, end);}/** * 快速排序 * @param arr 数组 * @param n 数组元素个数 */void quickSort(int arr[], int n) {    quick(arr, 0, n - 1);}

循环实现

int Partition(int *arr, int start, int end) {    if (start == end)        return start;    int left = start, right = end, key = arr[start];    while (left < right) {        while (left < right && arr[right] >= key)            right--;        arr[left] = arr[right];        while (left < right && arr[left] <= key)            left++;        arr[right] = arr[left];    }    arr[left] = key;    return left;}/** * 非递归实现快排 */void quickSort2(int arr[], int n) {    stack<int> st;    st.push(0);    st.push(n-1);    while (!st.empty()) {        int right = st.top();        st.pop();        int left = st.top();        st.pop();        int mid = Partition(arr, left, right);        if (mid - 1 > left) {            st.push(left);            st.push(mid - 1);        }        if (mid + 1 < right) {            st.push(mid + 1);            st.push(right);        }    }}
堆排序
/** * 调整大顶堆 * @param arr * @param i * @param len */void adjust(int arr[], int i, int len) {    int temp = arr[i];    // 从i结点的左子结点开始,也就是2i+1    for (int k = 2 * i + 1; k < len; k = k * 2 + 1) {        // 如果左子结点小于右子结点,k指向右子结点        if (k + 1 < len && arr[k] < arr[k + 1])            k++;        // 如果子结点大于父节点,将子结点的值赋给父节点        if (arr[k] > temp) {            arr[i] = arr[k];            i = k;        } else {            break;        }    }    arr[i] = temp;}/** * 堆排序 * @param arr 数组 * @param n 数组元素个数 */void headSort(int arr[], int n) {    int i, temp;    // 1.构建大顶堆    for (i = n / 2; i >= 0; --i) {        // 从第一个非叶子结点从下至上,从右至左调整结构        adjust(arr, i, n);    }    // 2.调整堆结构并交换堆顶元素与末尾元素    for (int j = n - 1; j > 0; --j) {        swap(arr[0], arr[j]);        adjust(arr, 0, j);    }}

回溯

四皇后问题

在一个4×4的国际象棋棋盘上,一次一个地摆放4枚皇后棋子,摆好后满足每行、每列和对角线上只允许出现一枚棋子,即棋子间不许相互俘获。

解决策略:

  1. 初始化一个4x4的数组Q,Q[i][j]=1表示该点有皇后,无皇后的点置为0。
  2. 遍历每一行,使用isCorrect判断当前点是否可以放置皇后,可以放置后使用递归将皇后数量+1
  3. 确定四个皇后之后将上个确定的皇后置为0进行回溯,继续执行循环。
// 判断皇后是否可以放置到[i][j]int isCorrect(int i, int j, int (*QueenArray)[4]) {    int s, t;    // 判断行    for (s = i, t = 0; t < 4; ++t)        if (QueenArray[s][t] == 1 && t != j)            return 0;    // 判断列    for (s = 0, t = j; s < 4; ++s)        if (QueenArray[s][t] == 1 && s != i)            return 0;    // 判断左上    for (s = i - 1, t = j - 1; s >= 0 && t >= 0; s--, t--)        if (QueenArray[s][t] == 1)            return 0;    // 判读右上    for (s = i - 1, t = j + 1; s >= 0 && t < 4; s--, t++)        if (QueenArray[s][t] == 1)            return 0;    // 判断左下    for (s = i + 1, t = j - 1; s < 4 && t >= 0; s++, t--)        if (QueenArray[s][t] == 1)            return 0;    // 判读右下    for (s = i + 1, t = j + 1; s < 4 && t < 4; s++, t++)        if (QueenArray[s][t] == 1)            return 0;        return 1;}
/** * 回溯法解决4皇后问题 * @param queenNum 当前皇后数量 * @param QueenArray 皇后数组 */void Queen(int queenNum, int (*QueenArray)[4]) {    int i, k;    // 四个皇后都确定    if (queenNum == 4) {        for (i = 0; i < 4; i++) {            for (k = 0; k < 4; k++)                cout << QueenArray[i][k] << ' ';            cout << endl;        }        cout << endl;        count++;        return;    }    for (i = 0; i < 4; i++) {        if (isCorrect(i, queenNum, QueenArray)) {            QueenArray[i][queenNum] = 1;            Queen(queenNum+1, QueenArray);            QueenArray[i][queenNum] = 0;        }    }}

动态规划

上台阶问题

有一楼梯共m级,若每次只能跨上一级或二级,要走上第m级,共有多少走法

/** * 上台阶问题 * @param n 台阶数 * @return */int UpStairs(int n) {    if (n < 0)        return -1;    if (n == 1)        return 1;    if (n == 2)        return 2;    return UpStairs(n - 1) + UpStairs(n - 2);}
// 使用动态规划解决int UpStairs(int n) {    if (n < 0)        return -1;    if (n == 1)        return 1;    if (n == 2)        return 2;    int *dp = new int[n+1];    dp[0] = 0;    dp[1] = 1;    dp[2] = 2;    for (int i = 3; i <= n; ++i)        dp[i] = dp[i-1] + dp[i-2]    return dp[n];}
编辑距离

已知两个字符串word1和word2,求从word1转化成word2最少需要几步。其中,每一步只能进行以下三个操作之一:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

解决策略:

  1. 使用一个二维数组dp记录需要的操作数
  2. dp[i][j]表示word1转化为word2需要的最小操作数,有两种状态,word1[i]==word2[j]和word1[i]!=word2[j],
    • word1[i] == word2[j],则dp[i][j] = dp[i-1][j-1],
    • word1[i] !=wrod2[j],有三条路径:
      1. 比如,”xyz” => “efg” 的最小编辑距离等于 “xy” => “efg” 的最小编辑距离 + 1(因为允许插入操作,插入一个 “z”),抽象的描述便是 d[m][n] === d[m-1][n] + 1。
      2. 比如,”xyz” => “efg” 的最小编辑距离等于 “xyzg” => “efg” 的最小编辑距离 + 1,且因为最后一个字符都是 “g”,根据第一个判断条件,可以再等于 “xyz” => “ef” 的最小编辑距离 + 1,因此,得到结论:”xyz” => “efg” 的最小编辑距离等于 “xyz” => “ef” 的最小编辑距离 + 1,抽象的描述便是:d[m][n] === d[m][n-1] + 1。
      3. 比如,”xyz” => “efg” 的最小编辑距离等于 “xyg” => “efg” 的最小编辑距离 + 1(因为允许替换操作,可以把 “g” 换成 “z”),再等于 “xy” => “ef” 的编辑距离 + 1(根据第一个判断条件),抽象的描述便是: d[m][n] === d[m-1][n-1] + 1。
        上述三种情况都有可能出现,因此,取其中的最小值便是整体上的最小编辑距离。
/** * 编辑距离 */int miniDistance(string word1, string word2) {    int n1 = word1.size();    int n2 = word2.size();    // vector <vector <int>> dp[n1][n2];    // 开辟空间    int **dp = new int*[n1+1];    for (int i = 0; i <= n1; ++i)        dp[i] = new int[n2+1]();    // 初始化    for (int i = 0; i < n1; ++i)        dp[i+1][0] = dp[i][0] + 1;    for (int i = 0; i < n2; ++i)        dp[0][i+1] = dp[0][i] + 1;        for  (int i = 1; i <= n1; ++i) {        for (int j = 1; j <= n2; ++j)            if (word1[i-1] == word2[j-1])                dp[i][j] = dp[i-1][j-1];            else                 dp[i][j] = min(min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1]) + 1;    }    // 释放空间    for (int i = 0; i < n1; ++i)        delete[] dp[i];    delete[] dp;    return dp[n1][n2];}

贪心

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值