数据结构学习笔记(七)搜索结构

2023-12-19


1. 前言

本系列笔记基于 清华大学出版社的《数据结构:用面向对象方法与C++语言描述》第二版进行学习。

2. 概念

使用基于关键码的搜索,搜索结果应该是唯一的。使用基于属性的搜索方法,搜索结果可能不唯一。
搜索的环境有两种,静态环境和动态环境。区别在于插入和删除后搜索结构会不会变化。
衡量搜索的时间效率的标准是:
在搜索过程中关键码的平均比较次数或平均读写磁盘次数,成为平均搜索长度ASL。

3 静态搜索结构

3.1 静态搜索表

最简单的基于数组的数据表类。

#include <iostream>
#include<assert.h>
using namespace std;
const int defaultSize = 100;
class dataList;

class dataNode {
    friend class dataList;
public:
    dataNode(const int x = 99):key(x),data(0){}
    int getKey() const { return key; }
    void setKey(int k) { key = k; }
private:
    int key;                  // 关键码
    int data;
};

class dataList {
public:
    dataList(int sz = defaultSize) :ArraySize(sz), CurrentSize(0) {
        Element = new dataNode[sz];
        assert(Element != NULL);
    }
    dataList(dataList& R);
    virtual ~dataList() { delete[]Element; }                    
    virtual int Length() { return CurrentSize; }               
    virtual int getKey(int i)const {
    // 提取第i个元素值(从1开始)
        assert(i > 0 || i <= CurrentSize);
        return Element[i - 1].key;
    }         
    virtual void setKey(int x, int i) {         // 修改第i个元素的值
        assert(i > 0 || i <= CurrentSize);
        Element[i - 1].key = x;
    }
    virtual int SeqSearch(const int x) const;       // 搜索
    virtual bool Insert(int& e1);                       // 插入
    virtual bool Remove(const int x, int& e1);      // 删除
    friend ostream& operator<<(ostream& out, const dataList& OutList);
    friend ostream& operator>>(istream in, dataList& InList);
protected:
    dataNode* Element;
    int ArraySize;
    int CurrentSize;
};

class searchList :public dataList {
public:
    searchList(int sz = defaultSize) :dataList(sz){}
    virtual int SeqSearch(const int x)const;
};

int main()
{
    std::cout << "Hello World!\n";
}

bool dataList::Insert(int& e1)
{
    if (CurrentSize == ArraySize) return false;   // 表满,无法插入
    Element[CurrentSize] = e1;
    CurrentSize++;
    return true;
}

bool dataList::Remove(const int x, int& e1)
{
    if (CurrentSize == 0)return false;
    int i;
    for (i = 0; i < CurrentSize && Element[i].key != x; i++);
    if (i == CurrentSize) return false;
    e1 = Element[i].data;
    Element[i] = Element[CurrentSize - 1];
    CurrentSize--; return true;
}

ostream& operator<<(ostream& out, const dataList& OutList)
{
    cout << "Array Contents:\n";
    for (int i = 1; i < OutList.CurrentSize; i++) {
        cout << OutList.CurrentSize; 
        i++;
    }
    cout << endl;
    cout << "Array Current Size:" << OutList.CurrentSize << endl;
    return out;
}

ostream& operator>>(istream in, dataList& InList)
{
    cout << "Enter array Current Size:" << endl;
    in >> InList.CurrentSize;
    cout << "Enter array Elements:\n";
    for (int i = 1; i <= InList.CurrentSize; i++) {
        cout << "Element" << i << ":";
        in >> InList.Element[i - 1];
    }
    return in;
}

编译不过,因为Elements中没有设置接触到数据的函数,可以自行添加

3.2 顺序搜索表

int searchList::SeqSearch1(const int x, int loc) const
{
    if (loc > CurrentSize) return 0;
    else if (Element[loc - 1].key == x) return loc;
    else return SeqSearch1(x, loc + 1);
}

3.2.1 基于有序顺序表和顺序搜索和折半搜索

①顺序搜索

class SortedList : public searchList {
public:
    SortedList(int sz=100):searchList(sz){}
    ~SortedList(){}
    int SequentSearch(const int x)const;            // 顺序搜索
    int BinarySearch(const int x)const;                // 折半搜索
    bool Insert(const int& e1);                            // 插入
    int Begin() { return (CurrentSize == 0) ? 0 : 1; }      // 定位第一个
    int Next(int i) { return(i >= 1 && i <= CurrentSize) ? i + 1 : 0; } // 定位下一个
};

int SortedList::SequentSearch(const int x) const
{
    for (int i = 1; i <= CurrentSize; i++)
        if (Element[i - 1].key == x) return i;            // 成功,停止搜索
        else if (Element[i - 1].key > x)break;          // 不成功,停止搜索
    return 0;
}

②折半搜索
折半搜索又称二分搜索法
在这里插入图片描述
在这里插入图片描述
非递归算法

int SortedList::BinarySearch(const int x) const
{
    // 非递归算法
    int high = CurrentSize - 1;
    int low = 0;
    int mid;

    while (low <=high)          // 如果没有,最后搜索的一次是low<=high
    {
        mid = (low + high) / 2;
        if (x > Element[mid].key) low = mid + 1;            // 把中间的元素的后一位变成low
        else if (x < Element[mid].key) high = mid - 1;      // 把中间元素的前一位变成high
        else return mid + 1;
    }

    return 0;
}


递归算法

int SortedList::BinarySearch1(const int x, int low, int high) const
{
    int mid = 0;
    if (low <= high) {
        mid = (low + high) / 2;
        if (x > Element[mid - 1].key)
            mid = BinarySearch1(x, mid + 1, high);          
        else if (x < Element[mid - 1].key)
            mid = BinarySearch1(x, low, mid - 1);
    }
    return mid;
}

③插入算法

void SortedList::Insert(const dataNode& e1)
{
    assert(CurrentSize == ArraySize);
    int i = 1;
    while (i <= CurrentSize && Element[i - 1].key <= e1.key)i++;            // 找到小于插入的结点的最大的结点
    for (int j = CurrentSize; j >= i; j--) Element[j] = Element[j - 1];          // 前面的赋值给另一个
    Element[i - 1] = e1;
    CurrentSize ++;
}

4 二叉搜索树

在这里插入图片描述
简单来说,就是如果找到二叉树的中序遍历,他的中序遍历一定是从小到大排序的
在这里插入图片描述
如上图,其中序遍历均位123

4.1 搜索二叉树的类定义

struct BSTNode
{
    int data;               // 二叉树节点类
    BSTNode* left, * right; // 左右子女
    BSTNode():left(NULL),right(NULL){}
    BSTNode(const int d, BSTNode* L = NULL, BSTNode*R =NULL):data(d),left(L),right(R){}
    ~BSTNode(){}
    void SetData(int d) { data = d; }
    int getData() { return data; }
};

class BST {
public:
    BST() :root(NULL) {}                                                
    BST(int Value);                               
    ~BST() {};
    bool Search(const int x) {
        return (search(x, root) != NULL) ? true : false;
    }
    BST& oprator = (const BST & R);         // 赋值
    void makeEmpty() { makeEmpty(root); root = NULL; }
    void PrintTree()const { PrintTree(root); }
    int Min() { return Min(root)->data; }
    int Max() { return Max(root)->data; }
    bool Insert(const int& e1) { return Insert(e1, root); }
    bool Remove(const int x) { return Remove(x, root); }

private:
    BSTNode* root;
    int RefValue;               // 停止标志
    BSTNode* search(const int x, BSTNode* ptr);         // 搜索
    void makeEmpty(BSTNode*& ptr);                          // 置空
    void PrintTree(BSTNode* ptr)const;                          // 打印
    BSTNode* Copy(const BSTNode* ptr);                      // 复制
    BSTNode* Min(BSTNode* ptr)const;                                  // 求最小
    BSTNode* Max(BSTNode* ptr)const;                                 // 求最大
    bool Insert(const int& e1, BSTNode*& ptr);                      // 递归插入
    bool Remove(const int x, BSTNode*& ptr);                        // 删除
};

4.2 搜索二叉树的搜索

在这里插入图片描述
超级好理解

BSTNode* BST::search(const int x, BSTNode* ptr)
{
    if (ptr == NULL) return NULL;
    else if (x < ptr->data) return search(x, ptr->left);
    else if (x > ptr->data) return search(x, ptr->right);
    else return ptr;
}

4.3 搜索二叉树的插入

在这里插入图片描述

bool BST::Insert(const int& e1, BSTNode*& ptr)
{
    if (ptr == NULL) {
        ptr = new BSTNode(e1);
        if (ptr = NULL) { cout << "out of space" << endl; exit(1); }
    }
    else if (e1 < ptr->data) Insert(e1, ptr->left);         // 比结点小存左边
    else if (e1 > ptr->data)Insert(e1, ptr->right);   // 比节点小存右边
    else return false;              
}

4.4 搜索二叉树的删除

在这里插入图片描述

bool BST::Remove(const int x, BSTNode*& ptr)
{
    BSTNode* temp;
    if (ptr != NULL) {
        if (x < ptr->data) Remove(x, ptr->left);            // 左子树执行删除
        else if (x > ptr->data) Remove(x, ptr->right);    // 在右子树执行删除
        else if (ptr->left != NULL && ptr->right != NULL) { // 有两个子女节点
            temp = ptr->right;                                              // 右子树搜寻中序的第一个结点
            while (temp->left != NULL) temp = temp->left;
            ptr->data = temp->data;     // 用该节点数据代替根节点数据
            Remove(ptr->data, ptr->right);
        }
        else {      // 只有一个子女结点(左子结点或右子结点)           这部分是为了链接删除部分缺掉的那条线
            temp = ptr;
            if (ptr->left == NULL) ptr = ptr->right;
            else ptr = ptr->left;
            delete temp;
            return true;
        }
        }
    return false;
    }

5 AVL树

任一结点的右子树的高度减去左子树的高度差只能为-1,0,1

在这里插入图片描述

5.1 平衡化旋转

使不平衡的二叉搜索树变得平衡
先说插入
这里有四种情况

5.1.1 右旋:LL型状态

在这里插入图片描述
就是插入根节点的子结点的左子结点,
右旋相当于把树往顺时针旋转以下,把B点做成根结点,再把B结点的右子结点变成原根节点的左子节点,

旋转后变成:
在这里插入图片描述

5.1.2 左旋:RR型状态

左旋和右旋差不多,就是插入了根节点的右子结点的右子树中。
在这里插入图片描述
左旋相当于将整个树逆时针旋转了一下,将C点置为根结点,然后将A点作为C点的左子树,把原来的左子树3作为原根节点的右子树
在这里插入图片描述

5.1.3 右旋(LL)的例子

在这里插入图片描述
本次是插入0到原来的平衡树中,如图,属于LL情况,所以进行右旋操作。
找到不平衡的根结点4。所以我们要调整的树就是
在这里插入图片描述
将原有的左子树,以2为根节点的树往上提,作为这棵树的新根节点,并把原有根节点4作为2的右子树,把2的原右子变为原根节点4的左子树
在这里插入图片描述
再把2作为原来根节点4,进行连接
在这里插入图片描述

5.1.4 先左旋再右旋(LR)的操作

在这里插入图片描述

元素落到56都是一样的处理,把
D转换成新的根结点,把原有不平衡树的根节点A的左子女结点作为D的左子女结点,根节点A作为D的右子女结点。并把D的左子树当作B的右子树,把D的右子树当作A的左子树。
在这里插入图片描述

5.1.5 先右旋再左旋(RL)的操作

在这里插入图片描述

插入56所作的操作都一样,原理与LR差不多,把D作为新的根节点,把不平衡树的根节点A作为D的左子树,A的右子女结点C作为D的右子树。并把D的左子树转换成A的右子树,D的右子树转换为C的左子树。
在这里插入图片描述

5.1.6 RL操作的例子

在这里插入图片描述
把4当作新父结点,将不平衡树的根节点2作为4的左子树,根节点的右结点5作为4的右子树。把4的左子树赋给2作为其右子女结点。

在这里插入图片描述
结果
在这里插入图片描述

5.1.7 代码实现

void AVLTree::RotateL(AVLNode*& ptr)
{
    // 右子树高RR型状态
    AVLNode* subL = ptr;        // 要旋转的结点,即不平衡的树的根结点
    ptr = subL->right;              // 原根节点的右结点
    subL->right = ptr->left;     // 原根子树的右子女转化成其右结点的左子树
    ptr->left = subL;                 // 把原根节点作为原根节点右结点的左子树,此时ptr为新根节点
    ptr->bf = subL->bf = 0;

}

void AVLTree::RotateR(AVLNode*& ptr)
{
    // 左子树高LL型状态
    AVLNode* subR = ptr;
    ptr = subR->left;
    subR->left = ptr->right;
    ptr->right = subR;
    ptr->bf = subR->bf = 0;
}
void AVLTree::RotateRL(AVLNode*& ptr)
{
    AVLNode* subL = ptr;                // 最后根节点的左子树
    AVLNode* subR = subL->right;    // 最后根节点的右子树
    ptr = subR->left;                           
    subR->left = ptr->right;                // subR的左子树变为其左子女节点的左子女节点
    ptr->right = subR;                          // 新根节点的右子女结点变为最终右子女结点
    if (ptr->bf >= 0) subR->bf = 0;
    else subR->bf = 1;
    subL->right = ptr->left;                // subL的右子女变为新根节点的左子女结点
    ptr->left = subL;                           // 新根结点的变为最终的左子女结点
    if (ptr->bf == 1) subL->bf = -1;
    else subL->bf = 0;
    ptr->bf = 0;
}

void AVLTree::RotateLR(AVLNode*& ptr)
{
    AVLNode* subR = ptr;
    AVLNode* subL = subR->left;
    ptr = subL->right;
    subL->right = ptr->left;
    ptr->left = subL;
    if (ptr->bf <= 0) subL->bf = 0;
    else subL->bf = -1;
    subR->left = ptr->right;
    ptr->right = subR;
    if (ptr->bf = -1) subR->bf = 1;
    else subR->bf = 0;
    ptr->bf = 0;
}

5.2 AVL树的插入

沿着其插入路线查各结点的平衡度
各结点会有三种情况
①bf = 0,不需要处理,结束平衡化
②bf = 1,不需要处理,继续回溯
③bf = 2,bf = 2,右子树高,需要结合右子女q的bf做处理
1)q的bf = 1,执行左单旋转,插入位置为RR
2)q的bf=-1,执行先右后左旋转,插入位置为RL
bf=-2,左子树高,需要结合左子女q的bf做处理
1)q的bf=1,执行右单旋转,插入位置为LL
2) q的bf=-1,执行先右旋后左旋,插入位置为LR

bool AVLTree::Insert(AVLNode*& ptr, int& e1)
{
    AVLNode* pr = NULL;
    AVLNode* p = ptr;
    AVLNode* q;
    int d;

    Stack st;
    while (p!=NULL)
    {
        if (e1 == p->data) return false;        // 找到了e1的结点,不插入
        pr = p;
        st.push(pr);                                        // 用栈记录查找路径,并找到插入的父结点pr
        if (e1 < p->data) p = p->left;
        else p = p->right;
    }

    p = new AVLNode(e1);
    if (p == NULL) { cout << "error when allocate memory!"; exit(1); }
    if (pr == NULL) { ptr = p; return true; }
    if (e1 < pr->data) pr->left = p;
    else pr->right = p;                             // 新结点插入
    while (st.Isempty() == false) {         // 重新平衡化
        st.Pop(pr);                                 // 调查父结点的平衡因子 
        if (p == pr->left) pr->bf--;        // 如果插入左边,则平衡因子-1
        else pr->bf++;                          // 插入右边则+1,平衡因子高度右边-左边
        if (pr->bf == 0) break;              // 第一种情况,平衡退出
        if (pr->bf == 1 || pr->bf == -1)
            p = pr;                                     // 第二种情况,继续向上搜寻
        else {                                              // 第三种情况,|bf| =2;
            d = (pr->bf < 0) ? -1 : 1;          // 区别单双旋
            if (p->bf == d) {                       // 两节点的平衡因子同号,单旋转
                if (d == -1) RotateR(pr);       // bf ==-1
                else RotateL(pr);                   // bf =1
            }
            else {
                if (d == -1) RotateLR(pr);
                else RotateRL(pr);
            }
            break;

        }
        if (st.IsEmpty() == true) ptr = pr;     // 调整到树的根节点
        else {
            st.getTop(q);
            if (q->data > pr->data) p->left = pr;
            else q->right = pr;
        }

    }

    return true;
}

这是>>和<<的重载:

istream& operator>>(istream in, AVLTree& Tree)
{
    int item;
    cout << "Construct AVL tree:\n";
    cout << "Input Data(end with" << Tree.RefValue << "):";
    in >> item;
    while (item != Tree.RefValue) {
        Tree.Insert(item);
        cout << "Input Data(end with" << Tree.RefValue << "):";
        in >> item;
    }
    return in;
}

ostream& operator<<(ostream& out, const AVLTree& Tree)
{
    out << "Inorder Traversal of AVL tree.\n";
    Tree.Traverse(Tree.root, out);              // 以中序次序输出个各节点的数据
    out << endl;
    return out;
}

在这里插入图片描述
看懂这个基本就学会了插入了

5.3 AVL树的删除

AVL树的删除算法与二叉搜索树类似,删除后如果破坏平衡性质,还需要做旋转
(1)如果被删结点p有两个子女,p的中序次序下找到直接前驱q,把q的内容传给p,把结点q当作被删结点p,他是只有一个子女的结点,此时看第二种情况
(2)如果被删结点p最多只有一个子女q。可以当p的父结点pr中原来指向p的指针改到q,如果结点p没有子女,p父结点pr的相应指针置位NULL。将原来的结点pr为根的子树的高度-1,并沿pr通向根的路径考量一路上各个结点的影响。
考查结点q的父结点pr。如果q是pr的左子女,则因子bf+1,否则bf-1
此时有三种情况
【1】bf的原来的平衡因子为0,则不需要调整了,结束重新平衡
在这里插入图片描述

【2】bf不为0,需要考察结点pr的父结点的平衡状态,
在这里插入图片描述
【3】需要平衡化
考察父结点pr的更高的子树的根为q(未被缩短的树),根据q的平衡因子,有三种操作
①如果q的 平衡因子为0 ,执行一个单旋(未被缩短的树,结束后可以结束平衡的过程
在这里插入图片描述
②如果q的平衡因子与pr的平衡因子 正负号相同 ,则执行一个单旋来恢复,结束后需要沿上继续评估平衡状态,相当LL型和RR型
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
③如果pr与q的平衡因子 正负号相反 ,则需要执行双旋q,相当于LR和RL型,同时继续向上评估
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

bool AVLTree::Remove(AVLNode*& ptr, int x, int& e1)
{
    // 删除关键码为x的结点
    AVLNode* pr = NULL;
    AVLNode *p = ptr;
    AVLNode* q;
    AVLNode* ppr;
    int d;
    int dd = 0;

    Stack* st;

    while (p != NULL) {
        // 寻找删除位置
        if (x == p->data) break;
        pr = p;
        st.push(pr);
        if (x < p->data) p = p->left;
        else p = p->right;
    }
    if (p == NULL) return false;            // 未找到,删除
    if (p->left != NULL && p->right != NULL) {  // 有两各子女的情况
        pr = p;
        st.push(pr);
        q = p->left;                // 再p左子树找到中序遍历直接前驱
        while (q->right != NULL) {
            pr = q;
            st.push(pr); 
            q = q->right;      
        }
        p->data = q->data;                  // 用q的值填补p
        p = q;                                      // 被删结点转为q,此时是只有一个子女结点q的情况
    }


    if (p->left != NULL) q = p->left;       // 被删结点只有一个子女
    else q = p->right;                              // 找到这个子女
    if (pr == NULL) ptr = q;    // 被删结点为根节点
    else {                                  // 被删结点不是根节点
        if (pr->left == p) pr->left = q;        // 把父结点连接到被删结点的子女结点
        else pr->right = q;
        while (st.IsEmpty()== false)            // 重新平衡化
        {
            st.Pop(pr);             
            if (pr->right == q) pr->bf--;       // 调整父结点的平衡因子
            else pr->bf++;
            if (st.IsEmpty() == false) {
                st.getTop(ppr);
                dd = (ppr->left == pr) ? -1 : 1;        // 旋转后和上层连接方向
            }
            else dd = 0;                                          // 旋转后不与上层连接
            if (pr->bf == 1 || pr->bf == -1) break;     // 不需要旋转,直接退出
            if (pr->bf != 0) {
                if (pr->bf < 0) { d = -1; q = pr->left; }
                else { d = 1; q = pr->right; }
                if (q->bf == 0) {
                    if (d == -1) {
                        RotateR(pr);
                        pr->bf = 1;
                        pr->left->bf = -1;
                    }
                    else {
                        RotateL(pr);
                        pr->bf = -1;
                        pr->right->bf = 1;
                    }
                    break;

                }
                if (q->bf == d) {
                    if (d == -1) RotateR(pr);
                    else RotateL(pr);
                }
                else {
                    if (d == -1) RotateLR(pr);
                    else RotateRL(pr);
                }
                if (dd == -1) ppr->left = pr;
                else if (dd == 1) ppr->right = pr;

            }
            q = pr;


        }
        if (st.IsEmpty() == true) ptr = pr;


    }

    delete p;

    return true;
}

不懂这段代码,真的很痛苦看的我

6 伸展树

每次插入结点,所有结点都要调整到根结点。
伸展树

6.1 插入

插入位置为根的左节点,右旋
插入位置为根的右结点,左旋
最左边LL情况,对中间结点进行右旋,在对结点进行右旋,直到变成根节点
最右边RR情况,对中间结点进行左旋,在对结点进行左旋,直到变成根节点
LR情况,中间结点先左旋后右旋,再对结点左旋右旋,直到变为根节点
RL情况,中间结点先右旋后左旋,再对结点右旋左旋,直到变为根节点

6.2 查找

插入和查找操作,查找后把查找到的结点变为根节点,情况跟插入类似

6.3 删除

和二叉搜索树相同,但需要把被删除结点的父结点展开到根节点。

7 红黑树

在这里插入图片描述
1)外部结点是黑色(空指针们
2)没有两个连续结点是红色,(可以两个连续结点是黑色
3)根到外部节点的路径上都有相同的黑色结点
在这里插入图片描述
也可以从指针看
在这里插入图片描述

在这里插入图片描述

7.1 红黑树搜索

和二叉搜索树完全相同

7.2 插入和删除

这个书上标**了,我这边先不学,看了下内容挺多的,后面再补

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构学习笔记(七)搜索结构 的相关文章

  • 2.[BUU]rip

    1 检查文件 checksec 一下 查看之后发现是64位 直接放入IDA64进行反编译 2 IDA反编译 进行分析 发现是gets 函数可以造成栈溢出 从而可以覆盖栈上的内容 想法 通过gets函数 栈溢出 来篡改栈上的内容指令 从而达到
  • 网络安全日报 2023年12月19日

    1 谷歌Chrome测试新的跟踪保护可阻止第三方Cookie https blog google products chrome privacy sandbox tracking protection Google 周四宣布将开始测试一项名

随机推荐

  • 能够图片转word工具推荐哪一个?这些一定要试试看

    临近年末 公司上下都在为了年末盘点而忙到飞起 大家都被分配到满满的任务 我也不例外 老板交代让我把一些图片文件整理成电子文档 方便后续内容的编辑和修改 本来工作就多 加上老板的任务要得急 我也没想那么多 直接就把老板发过来的图片资料全部一股
  • 测试编排效率提升:掌握前置接口响应数据的引用方法

    宁波银行深圳分行综合柜员面经 三方寄过去了 告诉我停止24届招聘 全部毁约 牛的 he芯 毁约应届生 34316 地产投资岗上岸啦 经验分享 江西农发行市级支行信贷岗面试 最终还是搏了一把 2023届CV视觉算法岗求职记录贴 规划指南 实习
  • 计算机SSM毕设选题 在线音乐网站系统(含源码+论文)

    文章目录 1 项目简介 2 实现效果 2 1 界面展示 3 设计方案 3 1 概述 3 2 系统流程 3 2 1 系统开发流程
  • Redis HyperLogLog:数据统计的轻量级解决方案

    引言 在现代数据驱动的应用中 Redis 以其出色的性能和灵活性成为了不可或缺的工具 特别是在统计大量数据时 传统的计数方法往往既耗时又占用大量存储空间 这次 阿七将介绍一种名为 HyperLogLog 的算法 它在 Redis 中的实现让
  • “最美中国女生”——AI绘画还没上车的有难了!!!

    废话少说 先上图 前段时间 ChatGPT生成了一张自诩为 最美的中国女生 虽然审美是主观的 但不可否认 图片的客观美 不过 你不会还只是看到图片里的美女 肤浅地欣赏高颜值吧 别人都已经透过现象看本质 赶上新浪潮 get新技能了 好 进入正
  • 底层的“原始码农”注定会被ChatGPT取代!

    抓不住AI的风口 你就等着被淘汰吧 绝对不是危言耸听 时代车轮滚滚向向前 没有跟上节奏 等你回过神来 才发现时代已经换新篇 掂量掂量自己的斤两 不禁倒吸一口凉气 想想 取代 二字是何其刺耳又真实 遇到问题解决问题 那么 如何拥有不可替代性成
  • 计算机网络基础 走入计算机行业的重中之重

    计算机网络是指将地理位置不同的 功能独立的多台计算机通过通信线路连接起来 以功能完善的网络软件支撑 实现资源共享和信息传递的系统 对于信息安全高级工程师来说 理解计算机网络的基本原理是至关重要的 下面我们将逐一解析计算机网络的核心概念 网络
  • CCF编程能力等级认证GESP—C++5级—样题1

    CCF编程能力等级认证GESP C 1级 样题1 单选题 每题 2 分 共 30 分 判断题 每题 2 分 共 20 分 编程题 每题 25 分 共 50 分 小杨的锻炼 小杨的队列 参考答案 单选题
  • 【网络安全】—Shell编程入门(1)

    Shell编程入门 Shell 是 Unix Linux 操作系统下的一种命令行解释器 它接收用户输入的命令然后调用相应的程序 我们可以通过 Shell 脚本来自动执行一系列的命令 接下来 我们将详细介绍 Shell 编程的基本概念和技能
  • 新入门IT行业想学编程?你可以选Python!

    最近有不少新入门IT行业的同学问小编 Python是什么 所以 小编给大家准备好了一份Python学习攻略 我们一起来看一下吧 01 Python是什么 Python由荷兰数学和计算机科学研究学会的Guido van Rossum 于199
  • uniapp H5项目使用ucharts的Echart组件方式创建圆环

    问题 没有报错但是图表不出来 调试了半天圆环图表没有不出来 是因为没有明示设置宽度与高度 请根据实际需求修改父元素尺寸 组件自动识别宽高 charts box width 100 height 300px 最终效果 先导入ucharts到项
  • 软件测试/测试开发/人工智能/测试管理圆桌讨论会 | 解锁测试管理的核心问题,提升您的管理实力!

    人工智能的出现 对我们的测试行业有哪些明显的影响呢 我们又应该如何应对 如何更好地拥抱人工智能呢 如何打造积极向上 主动 执行力强 不推诿 不甩锅 服从安排 和谐 互帮互助的团队 如何有效的追踪团队的测试效率 后续对测试时间 质量等评估做支
  • 2023年12月中国数据库排行榜:人大金仓跻身前五,南大通用、中兴热度再升

    破冰行舟蓄势起 壮志猛发破浪飞 2023年12月的 墨天轮中国数据库流行度排行 火热出炉 本月共有288个数据库参与排名 本月排行榜前十名有所变动 人大金仓冲入前五开新局 南大通用 中兴通讯排名上升占优势 墨天轮十强名次归属再迎变动 本月排
  • 测试编排效率提升:掌握前置接口响应数据的引用方法

    宁波银行深圳分行综合柜员面经 三方寄过去了 告诉我停止24届招聘 全部毁约 牛的 he芯 毁约应届生 34316 地产投资岗上岸啦 经验分享 江西农发行市级支行信贷岗面试 最终还是搏了一把 2023届CV视觉算法岗求职记录贴 规划指南 实习
  • 浅谈专项测试之弱网络测试

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读1 9k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • unittest自动化测试断言方法的介绍

    断言如同在测试用例上 类似于预期结果与实际结果是否一致 如果一致则表示测试通过 Assert断言 很好的用于测试结果判断上 更灵活的对预期结果和实际结果进行对比 下面简单的介绍一下unittest的Assert断言 unittest中基础的
  • 干货:教你如何在JMeter中调用Python代码N种方法!

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读1 9k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • AI绘画生成器让你秒变画神,输入文字就能生成精美图片的AI绘画功能,AI绘画软件推荐

    在当今这个数字化时代 图片编辑已经成为我们日常生活和工作中的一项重要任务 无论是社交媒体上的个人展示 还是商业广告中的产品推广 精美的图片总能吸引更多的关注和赞誉 为了满足广大用户对图片编辑的需求 我们推出了一款全新的软件 首助编辑高手 1
  • CCF编程能力等级认证GESP—C++4级—20230923

    CCF编程能力等级认证GESP C 4级 20230923 单选题 每题 2 分 共 30 分 判断题 每题 2 分 共 20 分 编程题 每题 25 分 共 50 分 进制转换 变长编码 答案及解析 单选题
  • 数据结构学习笔记(七)搜索结构

    文章目录 1 前言 2 概念 3 静态搜索结构 3 1 静态搜索表 3 2 顺序搜索表 3 2 1 基于有序顺序表和顺序搜索和折半搜索 4 二叉搜索树