第2章顺序表与链表

顺序表

整体大小为9-size

元素个数为5-count

插入的时候size++,count不变

扩充:size不变,count加容量

结构定义:size count 连续存储区

realloc

  1. 直接往后扩容,没空间呢?
  2. 找一篇新的空间,并删除原内存,找不到这么大的?
  3. 直接返回null,且原内存不懂
c++ 复制代码
//
// Created by cry on 2024/3/6.
//
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#include <time.h>
#define MAX_OP 20
//结构定义+结构操作
//1.结构定义
//size count 连续存储区
typedef struct acm_1_1_List_vector{
    int size,count;
    int *data;
} acm_1_1_List_vector;
//返回大小为n的新顺序表
acm_1_1_List_vector *acm_1_1_List_getNewVector(int n){
    //开辟空间
    acm_1_1_List_vector *p=(acm_1_1_List_vector *)malloc(sizeof (acm_1_1_List_vector));
    p->size=n;
    p->count=0;
    p->data=(int *)malloc(sizeof(int) *n);
    return p;
}
//销毁,先销毁存储区然后销毁整个
void acm_1_1_List_clear(acm_1_1_List_vector *v){
    if(v==NULL) return;
    free(v->data);
    free(v);
    return ;
}
int acm_1_1_List_expand(acm_1_1_List_vector *v){
    if(v==NULL){return 0;}
    printf("expand v from %d to %d\n",v->size,2*v->size);
    //新定义一个指针,并把扩充后的这个值赋值给指针,不为空就给data
    int *p=(int *)(v->data,sizeof(int)*2*v->size); //进行重新分配内存大小
    if(p==NULL)return 0; //为NULL代表扩容失败
    v->data=p; //扩容成功
    v->size *=2;
    return 1;
}
//插入操作
int acm_1_1_List_insert(acm_1_1_List_vector *v,int pos,int val){
    if(pos <0 || pos >v->count) return 0;
    if(v->size==v->count && !acm_1_1_List_expand(v))return 0; //满了
    for(int i=v->count;i>=pos;i--){ //从后往前
        v->data[i+1] =v->data[i];
    }
    v->data[pos]=val;
    v->count+=1;
    return 1;
}
int acm_1_1_List_erase(acm_1_1_List_vector *v,int pos){
    if(pos <0 || pos >=v->count) return 0;
    for(int i=pos+1;i<v->count;i++){ //前移
        v->data[i-1]=v->data[i];
    }
    v->count-=1;
    return 1;
}
void acm_1_1_List_output_vector(acm_1_1_List_vector *v){
    int len=0;
    for(int i=0;i<v->size;i++){
        len+=printf("%5d",i);
    }
    printf("\n");
    for(int i=0;i<len;i++){
        printf("-");
    }
    printf("\n");
    for(int i=0;i<v->count;i++){
        printf("%5d",v->data[i]);
    }
    printf("\n");
    return;
}

void acm_1_1_List_test(){
    srand(time(0));
    int pos,val,op,ret;
    acm_1_1_List_vector *v=acm_1_1_List_getNewVector(2);
    for(int i=0;i<MAX_OP;i++){
        op=rand()%4; //决定插入还是删除
        switch(op){
            case 0: //0 1 2插入 ,3删除
            case 1:
            case 2:
                pos=rand()%(v->count+2);
                val=rand()%100;
                ret=acm_1_1_List_insert(v,pos,val);
                printf("insert %d at %d to vector=%d\n",
                       val,pos,ret);
                break;
            case 3:
                pos=rand()%(v->count+2);
                ret=acm_1_1_List_erase(v,pos);
                printf("erase item at %d in vector=%d\n",
                       pos,ret);
                break;

        }
        acm_1_1_List_output_vector(v);
        printf("\n\n");
    }

    acm_1_1_List_clear(v);
}

链表

数据结构=定义+操作

定义:数据,下一个节点地址

操作:插入删除

注意:错误的插入会导致内存泄漏(就是后面不用的变量且找不到地址,没有即时free)

链表分为 有头链表 和 无头链表

c++ 复制代码
//
// Created by cry on 2024/3/6.
//
#include <stdio.h>
#include <stdlib.h>
#include <ctime>

//结构定义
typedef struct Node{
    int data; //数据信息
    struct Node *next;//下一个节点
}Node;

//结构操作
Node *acm_1_2_Linked_getNewNode(int val){
    Node *p=(Node *)malloc(sizeof(Node));
    p->data=val;
    p->next=NULL;
    return p;
}
void acm_1_2_Linked_clear(Node *head){
    if(head==NULL) return ;
    //循环遍历链表的每一个节点,删除当前节点到下一个节点,直到p为空
    for(Node *p=head,*q;p;p=q){
        q = p->next;
        free(p);
    }
}

//插入
Node *acm_1_2_Linked_insert(Node *head,int pos,int val){
    //当链表为空的时候需要getNewNode
    if(pos==0){
        Node *p= acm_1_2_Linked_getNewNode(val);
        p->next=head;
        return p;
    }
    Node *p=head;
    for(int i=1;i<pos;i++){
        p=p->next; //找到待插入的位置
    }//结束后p为待插入节点的前一位
    Node *node= acm_1_2_Linked_getNewNode(val); //待插入新节点
    node->next=p->next;
    p->next=node;
    node->data=val;
    return head;
}
void acm_1_2_Linked_output_linklist(Node *head){
    int len=0,n=0;
    for(Node *p=head;p;p=p->next){
        n+=1;//求长度
    }
    for(int i=0;i<n;i++){
        printf("%3d",i);
        printf("  ");
    }
    printf("\n");
    for(Node *p=head;p;p=p->next) {
        printf("%3d", p->data);
        printf("->");
    }
    printf("\n\n\n");
    return;

}
void acm_1_2_Linked_test(){
    srand(time(0));
#define MAX_OP 20
    Node *head=NULL;
    for(int i=0;i<MAX_OP;i++){
        int pos=rand()%(i+1),val=rand()%100;
        printf("insert %d at %d to Linklist\n",val,pos);
        head=acm_1_2_Linked_insert(head,pos,val);
        acm_1_2_Linked_output_linklist(head);
    }
    acm_1_2_Linked_clear(head);
}

//普通查找
int acm_1_2_Linked_find(Node *head,int val){
    Node *p=head;
    while(p){
        if(p->data==val)return 1;
        p=p->next;
    }
    return 0;
}
int acm_1_2_Linked_huaYangFind(Node *head,int val){
    Node *p=head;
    int n=0;
    while(p){
        if(p->data==val){
            acm_1_2_Linked_output_linklist(head,1);
            //先输出整个链表
            //然后在对应位置输出小箭头
            int len=n*(DL+2)+2;
            for(int i=0;i<len;i++)printf(" ");
            printf("^\n");
            for(int i=0;i<len;i++)printf(" ");
            printf("|\n");
            return 1;
        }
        n+=1;
        p=p->next;
    }
    return 0;
}

双向链表,第一个指向最后一个节点

双向链表比普通链表多一个prev的数据定义

链表反转

c++ 复制代码
//  头插法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode new_head,*p=head,*q;
        new_head.next=NULL;
        while(p){
            q=p->next;
            p->next=new_head.next;
            new_head.next=p;
            p=q;
        }
        return new_head.next;
    }
};
//  递归法
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
       if(head==NULL || head.next==NULl) return head; //空和一个不用动
       ListNode *tail=head->next; 
       ListNode *new_head=reverseList(head->next);
       head->next=tail->next;
       tail->next=head; //默认前面都排序好了,直接将head插到最后
       return new_head;
    }
};

环形链表1

c++ 复制代码
class Solution {
public:
    bool hasCycle(ListNode *head) {
        //原理是,跑的快的比跑的慢的更快到目的地,表明没有环
        ListNode *p=head,*q=head;
        while(q && q->next){
            p=p->next; //跑的慢的
            q=q->next->next; //跑的快的
            if(p==q) return true; //遇见了,有环

        }
        return false; //快的到达终点
    }
};

判断循环就用快慢指针

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
c++ 复制代码
class Solution {
public:
    int getNext(int x){
        int d,y=0;
        while(x){
            d=x%10;
            y+=d*d;
            x/=10;
        }
        return y;
    }
    bool isHappy(int n) {
        int p=n,q=n;
        while(q!=1){
            p=getNext(p); //走一步
            q=getNext(getNext(q)); //走两步
            if(p==q && p!=1) return false;//相遇,不是快乐数
        }
        return true; //是快乐数;
    }
};

旋转链表

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
c++ 复制代码
class Solution {
public:
    //获取链表长度,直接遍历链表就行
    int getLength(ListNode *head){
        int n=0;
        while(head){
            n+=1;
            head=head->next;
        }
        return n;
    }
    ListNode* rotateRight(ListNode* head, int k) {
        if(head==NULL) return head; //空链表直接返回
        int n=getLength(head);
        k%=n; //取余数之后才是要移动的位数
        if(k==0) return head;
        ListNode *p=head,*q=head; //先找到倒数第k+1个结点
        for(int i=0;i<=k;i++){
            p=p->next;
        }
        while(p){ //当p指向NULL的时候,q指向的地方就是分割的地方
            p=p->next;
            q=q->next;
        } //p指向的就是第二部分的头结点
        p=q->next;
        q->next=NULL;
        q=p; //让q遍历一遍q的所有指针
        while(q->next !=NULL){
            q=q->next;
        } //q指向的节点就是第二部分最后一个节点
        q->next=head; //将q.next接到head
        return p;
    }
};

leetcode19:

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

c++ 复制代码
//双指针等距移动法
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode new_head,*p=&new_head,*q=p;//有头链表
        new_head.next=head;//pq都指向虚拟的头newHead
        //pq0(newhead)->head1->2->3->4
        for(int i=0;i<=n;i++){ //q指针向后移动n+1位数,因为是虚拟节点head
            q=q->next;
        }
        while(q)p=p->next,q=q->next; //如果q在NULL除,就两个指针同时向后移,靠后的q在空地址为止
        //等距指针法,因为是倒数
        p->next=p->next->next; //工程里面p->next需要回收掉,避免内存溢出
        //删除p指针后面的节点
        return new_head.next;
    }
};

leetcode142:环形链表2

环形链表1用快慢指针方法判断是否有环

c++ 复制代码
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *p=head,*q=head; //双指针
        while(q && q->next){ //p每次一步,q每次两步
            p=p->next;
            q=q->next->next;
            if(p==q)break; //直到pq相等为止
        }
        if(q==NULL || q->next==NULL) return NULL; //为NULL表示没环
        p=head; //讲一个撇回去
        while(p !=q){ //pq相同的速度往后走
            p=p->next;
            q=q->next;
        } //pq相交的位置就是环的位置,就是环的起始节点
        return p;
    }
};

92.反转链表2

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        if(left==1 && right==1) return head;
        if(left!=1){ //左区间
            //左右区间同时减一
            //将剩余链表部分和区间范围进行翻转,然后挂在头结点后面
            head->next=reverseBetween(head->next,left-1,right-1);
        }
        else{//右区间不为1
        //反转完以后的尾节点,和头结点地址
            ListNode *tail=head->next,*new_head;
            //对剩下部分进行反转
            new_head=reverseBetween(head->next,left,right-1);
            head->next=tail->next;
            tail->next=head;
            head=new_head;
        }
        return head;
    }
};
//左区间
            //左右区间同时减一
            //将剩余链表部分和区间范围进行翻转,然后挂在头结点后面
            head->next=reverseBetween(head->next,left-1,right-1);
        }
        else{//右区间不为1
        //反转完以后的尾节点,和头结点地址
            ListNode *tail=head->next,*new_head;
            //对剩下部分进行反转
            new_head=reverseBetween(head->next,left,right-1);
            head->next=tail->next;
            tail->next=head;
            head=new_head;
        }
        return head;
    }
};
相关推荐
dntktop4 分钟前
Converseen:全能免费批量图像处理专家
windows
aaasssdddd9616 分钟前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
芳菲菲其弥章21 分钟前
数据结构经典算法总复习(下卷)
数据结构·算法
yyyyyyykk23 分钟前
数据结构--链表
数据结构·链表
我是一只来自东方的鸭.34 分钟前
1. K11504 天平[Not so Mobile,UVa839]
数据结构·b树·算法
武昌库里写JAVA1 小时前
使用React Strict DOM改善React生态系统
数据结构·vue.js·spring boot·算法·课程设计
一个懒鬼2 小时前
Windows脚本清理C盘缓存
windows·缓存
清弦墨客2 小时前
【数据结构与算法】深度优先搜索:树与图的路径探寻之道
数据结构·python·算法·蓝桥杯·深度优先
尘觉2 小时前
算法的学习笔记—扑克牌顺子(牛客JZ61)
数据结构·笔记·学习·算法
蚁景网络安全3 小时前
Cobalt Strike 4.8 用户指南-第十四节 Aggressor 脚本
windows·microsoft