【数据结构】线性表

线性表

Part1

2010

// 1.基本设计思想
// 三次逆序法。将数组[x1,x2...xp]前p个逆序,再数组[xp+1,xp+2...xn]后n-p个逆序,最后数组整体逆序。

// 2.C/C++算法
void Reverse(int* arr, int l, int r) {
    while (l < r) {
        std::swap(arr[l++], arr[--r]);
    }
}
void ShiftP(int* arr, int n, int p) {
    Reverse(arr, 0, p);
    Reverse(arr, p, n);
    Reverse(arr, 0, n);
}

// 测试
int main() {
    int arr[]{ 1,2,3,4,5,6,7,8,9 };
    ShiftP(arr, 9, 5);
    for (int i = 0; i < 9; ++i)
        std::cout << arr[i] << ' ';
    std::cout << std::endl;
    return 0;
}

// 3.时间复杂度O(N),空间复杂度O(1)

2011

// 1.双指针法。指针pa指向数组a的起始位置,指针pb指向数组b的起始位置,设置cnt=n作为计数器,每当淘汰一个数不可能是中位数时cnt就减一
// 使用一个循环,循环内进行其中一个指针后移动,移动规则:若a[pa]
//      直至cnt==0,结束循环
// 最后进行中位数的判别:
//  1)若pa==0,说明b数组所有数都大于a数组,中位数就是b数组最后一个元素
//  2)若pb==0,说明a数组所有数都大于b数组,中位数就是a数组最后一个元素
//  3)否则,中位数就是max(a[pa-1],b[pb-1])

// 2.C/C++算法
 int FindMid(int* a, int* b, int n) {
    int pa = 0, pb = 0;
    int cnt = n;
    while (cnt) {
        if (a[pa] < b[pb]) {
            ++pa;
        } else {
            ++pb;
        }
        --cnt;
    }
    if (pa == 0) return b[n-1];         
    if (pb == 0) return a[n-1];
    return std::max(a[pa - 1], b[pb - 1]);
    
}

int main() {
    int a[]{ 11,13,15,17,19 };
    int b[]{ 2,4,6,19,20 };
    std::cout << FindMid(a, b, 5) << std::endl;
    return 0;
}

// 3.时间O(N),空间O(1)

2013

// 1.基本设计思想
// 由于a[i]
// 从前往后依次遍历数组a,
//  1)若a[i]<0,说明这个数字是用于计数的,跳过
//  2)若a[i]==i,即该数字首次出现且出现其应该被计数的位置,设置a[i]=-1,表示数字i出现了一次
// 用变量tmp=a[a[i]],记录其计数位置上的数字
//  3)若tmp<0,说明计数位上的数字用于计数,--a[a[i]],数字i出现的次数+1
//  4)若tmp>=0,a[a[i]]=-1进行数字i的首次计数,同时a[i]=tmp,将计数位上的数字移动到a[i]位置,同时--i,以便下次循环依旧对a[i],即目前的tmp进行计数




// 2.C/C++算法
int FindMajor(int* a, int n) {
    for (int i = 0; i < n; ++i) {
        if(a[i]<0) continue;
        if (a[i] == i) {
            a[i] = -1;
            continue;
        }
        int tmp = a[a[i]];
        if (tmp < 0) --a[a[i]];
        else {
            a[a[i]] = -1;
            a[i] = tmp;
            --i;
        }
    }
    int res = 0, cnt = 0;
    for (int i = 0; i < n; ++i) {
        if (a[i] < cnt) {
            res = i;
            cnt = a[i];
        }
    }
    if((-cnt)>n / 2)
        return res;
    return -1;
}
int main() {
    //int a[]{ 0,5,5,3,5,7,5,5 };
    int a[]{ 0,5,5,3,5,1,5,7 };
    std::cout << FindMajor(a, 8);
    return 0;
}


// 3.时间O(N),最坏情况下会循环2N次。空间O(1)

2018

// 1.基本设计思想
//  使用一个长度为n+2的辅助数组来标记对应的正整数是否出现,如vis[i]=1,表示数字i出现过,vis[i]=0,则表示数字i没出现过
// 随后从1开始遍历vis数字,遇到vis[i]==0,就说明找到了未出现的最小正整数


// 2.C/C++算法
int FindMinPos(int* a, int n) {
    vector<int> vis(n + 2, 0);
    for (int i = 0; i < n; ++i) {
        if (a[i] > 0 && a[i] <= n)
            vis[a[i]] = 1;
    }
    for (int i = 1; i <= n+1; ++i)
        if (vis[i]==0) return i;
    
    return -1;
}
int main()   
{
    //int a[]{ -5,3,2,3 };
    int a[]{ 1,2,3,4 };
    std::cout << FindMinPos(a, 4);
    return 0;
}

// 3.时间复杂度O(N),空间O(1)

2020

// 2020
// 1.算法设计思想
// 可以发现,距离D只会最小值和最大值有关,即D=(maxv-minv)*2
// 设置三个指针i,j,k分别指向三个数组的起始位置,每次找出三个中最小的元素和最大的元素,进行距离的计算,随后该数组上的指针++,指向下一个元素
//  一直循环,直到某一个数组遍历结束就停止,因为再往后距离差距只会越来越大,因为最小值已经固定了,最大值会越来越大

// 2.C/C++
int MinDistance(vector<int> s1, vector<int> s2, vector<int> s3) {
    int n1 = s1.size(), n2 = s2.size(), n3 = s3.size();
    int res = INT_MAX;
    int minv = INT_MAX, maxv = INT_MIN;
    int i, j, k;
    i = j = k = 0;
    while (i < n1 &&  j < n2 && k < n3) {   // 只要有一个数组遍历完了,就可以结束了,因为再往后差距只会越来越大
        minv = min({ s1[i],s2[j],s3[k] });
        maxv = max({ s1[i],s2[j],s3[k] });
        res = min(res, (maxv - minv) * 2);

        if (s1[i] == minv) {
            ++i;
        } else if (s2[j] == minv) {
             ++j;
        } else {
             ++k;
        }
    }
    return res;
}

int main() {
    vector<int> s1{ -1,0,9 }, s2{ -25,-10,10,-11 }, s3{ 2,9,17,30,41 };
    cout << MinDistance(s1, s2, s3);
    return 0;
}

// 3. 时间O(N)  空间O(1)

Part2

2009

// 1.先遍历一遍,求出链表的长度n,再从头遍历n-k个元素,此时即是倒数第k个元素

// 2.实现步骤:用一变量cur指向首个实际元素,同时使用变量n(初始化为0)开始计数链表的长度,使用cur=cur->link依次往后遍历
// n=n-k,此时n就是需要从前往后遍历的个数。cur=head->link,重新指向首个实际元素,遍历n个后,cur的指向即为倒数第k个节点,return cur->data即可

// 3.C/C++
struct node {
    int data;
    node* link;
    node(int da = 0, node* li = nullptr) :data(da), link(li) {}
};
int get_k_ele(node* head, int k) {
    node* cur = head->link;
    int n = 0;
    while (cur) {
        ++n;
        cur = cur->link;
    }
    n = n - k;
    cur = head->link;
    while (n--) {
        cur = cur->link;
    }
    return cur->data;
}

int main() {
    node* head = new node(0, new node(1, new node(2, new node(3, new node(4, new node(5, nullptr))))));
    cout << get_k_ele(head, 2) << endl;

    return 0;
}

2012

// 1.基本设计思想
// 设置双指针p1指向str1首实际元素,p2指向str2首实际元素,
// 使用while进行循环,循环条件为p1 != p2
//  p1和p2同时后移,
//  若某个时刻移动之后,p1为NULL,那么p1指向str2首实际元素
//  若p2为NULL,那么p2指向str2首实际元素
// 当循环终止后,p1的指向即为共同后缀的起始位置


// 2.C/C++

struct node {
    char data;
    node* link;
    node(int da = 0, node* li = nullptr) :data(da), link(li) {}
};

node* get_same_suffix(node* str1, node* str2) {
    node* p1 = str1->link;
    node* p2 = str2->link;

    while (p1 != p2) {
        if (p1 && p2) {
            p1 = p1->link;
            p2 = p2->link;
        }
        if (p1 == nullptr) {
            p1 = str2->link;
        } else if(p2==nullptr) {
            p2 = str1->link;
        }
    }
    return p1;
}

int main()   
{
    node* cur = new node('i', new node('n', new node('g')));
    node* str1 = new node(0,new node('l', new node('o', new node('a', new node('d', cur)))));
    node* str2 = new node(0,new node('b', new node('e', cur)));
    cout << get_same_suffix(str1, str2)->data << endl;
    return 0;
}



// 3.时间复杂度O(N),N为两条链表的长度之和,设str1表长为n1,str2表长n2,那么该算法固定遍历2*(n1+n2)次

2015

// 1.设计思想
// 设置一个标记数组,vis[i]=true表示绝对值是i的节点已近出现过了,初始全为false
// 从前往后遍历链表,若vis[abs(data)]==false,说明绝对值没有出现过的,进行标记vis[abs(data)]=true
//                  若vis[abs(data)]==true,绝对值出现过,进行删除

// 2.结点定义

struct node {
    int data;
    node* link;
    node(int da = 0, node* li = nullptr) :data(da), link(li) {}
};

// 3.C/C++

node* remove_same_node(node* head,int n) {
    vector<bool> vis(n + 1, false);
    node* cur = head; 
    while (cur && cur->link) {
        if (vis[abs(cur->link->data)]) {       // 绝对值相等的出现过,删除
            node* del = cur->link;
            cur->link = cur->link->link;
            delete del;
        } else {        // 没出现过,标记一下
            vis[abs(cur->link->data)] = true;
            cur = cur->link;
        }
    }
    return head;

}

int main()     
{
    node* head = new node(0, new node(1, new node(1, new node(3, new node(2, new node(3, nullptr))))));
    head = remove_same_node(head, 5)->link;
    while (head) {
        cout << head->data << ' ';
        head = head->link;
    }
    cout << endl;
    return 0;
}


// 4. 时间O(m),空间O(n)

2019


// 1.设计思想
// 使用快慢指针定位到链表的中间位置,再反转后半部分链表,随后用两个指针i,j,分别指向前半部分的表头和后半部分的表头,最后进行两个链表的交叉合并

// 2.C/C++

typedef struct node {
    int data;
    struct node* next;
    node(int da = 0, node* li = nullptr) :data(da), next(li) {}
}NODE;

NODE* re_sort(NODE* head) {
    NODE* fast = head, *low = head;
    // 使用快慢指针找到中间位置,循环退出之后,
    while (fast && fast->next) {
        fast = fast->next;
        low = low->next;
        if (fast) {
            fast = fast->next;
        }
    }
    // 此时low下一个结点即是后一半链表的首结点,这里规定后一半的长度为n/2(下取整)
    NODE* cur = low->next;
    low->next = nullptr;
    // 反转后半部分链表
    // 往后遍历,进行头插
    
    while (cur) {
        NODE* ne = cur->next;
        cur->next = low->next;
        low->next = cur;
        cur = ne;
    }
   
    low = low->next;
    cur = head->next;
    while (low) {
        NODE* low_next = low->next;
        low->next = cur->next;
        cur->next = low;
        cur = low->next;
        low = low_next;
    }
    if (cur) {
        cur->next = nullptr;
    }
    return head;
}

int main()     
{
    node* head = new node(0, new node(1, new node(2, new node(3, new node(4, new node(5, nullptr))))));
    head = re_sort(head)->next;
    while (head) {
        cout << head->data << ' ';
        head = head->next;
    }
    return 0;
}

// 3.时间O(N),空间O(1)

你可能感兴趣的:(数据结构,数据结构,408考研,C/CPP)