数据结构之队列和链表

一、队列:先进先出


1、使用数组模拟队列

队列是一个有序列表,可以使用数组或链表实现

一次性队列,不能复用:

  • 数据满了之后,条件:rear==maxSize-1,不会改变(rear++)
  • 数据取空了之后,条件:front==rear,不会改变(front++)

思路:队列(数组)最大长度:maxSize 队列首元素的前一个:front 队列最后元素(尾部):rear

1、数据入队(添加元素):

  1. 判断队列是否满-条件:rear==maxSize-1(rear < maxSize-1)
  2. 未满,则可添加数据,rear++

2、数据出队(取出元素):

  1. 判断队列是否为空-条件:front==rear
  2. 不为空,则可取出数据,front++

public class ArrayQueueDemo {
    public static void main(String[] args) {
        //创建一个队列
        ArrayQueue arrayQueue=new ArrayQueue(5);
        Scanner sc=new Scanner(System.in);
        while (true){
            System.out.println("1:显示队列");
            System.out.println("2:添加数据到队列");
            System.out.println("3:从队列取出数据");
            System.out.println("4:显示队列第一个元素");
            System.out.println("5:退出");
            int i = sc.nextInt();
            switch (i){
                case 1:arrayQueue.showQueue();break;
                case 2:
                    System.out.println("请输入一个数:");
                    int value = sc.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 3:try {
                    arrayQueue.getQueue();
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
                    break;
                case 4:try{
                    arrayQueue.headQueue();
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
                    break;
                case 5:System.exit(0);
            }
        }
    }

}

/*
    使用数组模拟队列:先进先出  一次性队列
    思路:队列(数组)最大长度:maxSize
         队列前一个元素:front
         队列最后元素(尾部):rear
    数据入队:1、判断队列是否满-条件:rear==maxSize-1
            2、若:rear < maxSize-1,则添加数据
    数据出队:
            1、判断队列是否为空-条件:front==rear
 */
class ArrayQueue{
    private int maxSize;//数组的长度
    private int front;//队列头(第一个数据的前一个)
    private int rear;//队列尾部(最后一个数据)
    private int[] arr;//存放数据,模拟队列

    //初始化队列
    public ArrayQueue(int arrMaxSize){
        maxSize=arrMaxSize;
        arr=new int[maxSize];
        front=-1;
        rear=-1;
    }

    //判断队列是否满
    public boolean isFull(){
        return rear==maxSize-1;
    }
    //判断队列是否空
    public boolean isEmpty(){
        return front==rear;
    }

    //添加数据到队列
    public void addQueue(int n){
        //判断队列是否满了
        if(isFull()){
            System.out.println("队列满了呀,不能添加数据哦!");
            return;
        }
        //未满,则添加
        rear++;//rear指针往后移
        arr[rear]=n;
        

    }

    //出队列
    public int getQueue(){
        //判断是否为空
        if (isEmpty()){
            //为空,则抛出异常
            throw new RuntimeException("队列是空的呀,不能取出数据!");
        }
        //若队列不为空
        front++;//front指针后移
        return arr[front];
    }

    //显示队列全部数据
    public  void showQueue(){

        if (isEmpty()){
            System.out.println("队列是空的呀,没有数据!");
        }
        //若队列不为空,则遍历队列(数组)
        for (int i = 0; i < arr.length; i++) {
            System.out.printf("arr[%d]=%d\n",i,arr[i]);
        }
    }

    //显示队列头数据
    public int headQueue(){
        if (isEmpty()){
            throw new RuntimeException("队列是空的呀,没有数据!");
        }
        return arr[front+1];
    }
}

2、实现数组的复用-环形数组

front:第一个元素,初始值=0

rear:尾指针的下一个元素,也就是预留出来的数组空间 初始值=0

​ 预留一个数组空间出来,比如队列的长度maxSize4,因为要预留一个空间,预留的空间为空,其实有效数据个数其实是3(对应数组下标为:0,1,2),当添加了三个数据时,rear3(其实就是预留空间的数组下标),由此可以判断数组是否满:(rear(3)+1)% maxSize(4) == front(0),如果队列已经满了,就不能添加数据到队列,此时rear=3;若此时取出一个数据,front0 变为 front 1,若再添加一个数据,此时条(rear(3)+1)% maxSize 0 不等于 front 1,判断满的条件不成立,可以添加,之前的rear3,此时rear0,添加之后已经三个元素了,满了。若再添加,根据条件:(rear(0)+1)% maxSize == 1front1,满的条件成立。数组的下标是动态的变化的

1、数据入队(添加元素)

  1. 判断是否满:(rear+1)% maxSize == front
  2. 未满:则rear=(rear+1)%maxSize,

2、数据出队(取出元素):

  1. 判断是否空: rear==front
  2. 不为空:front=(front+1)%maxSize

队列有效数据个数: (rear+maxSize-front)%maxSize

import java.util.Scanner;

public class CircleQueueDemo {
    public static void main(String[] args) {
        //创建一个队列
       CircleQueue queue=new CircleQueue(4);
        Scanner sc=new Scanner(System.in);
        while (true){
            System.out.println("1:显示队列");
            System.out.println("2:添加数据到队列");
            System.out.println("3:从队列取出数据");
            System.out.println("4:显示队列第一个元素");
            System.out.println("5:退出");
            int i = sc.nextInt();
            switch (i){
                case 1:queue.showQueue();break;
                case 2:
                    System.out.println("请输入一个数:");
                    int value = sc.nextInt();
                    queue.addQueue(value);
                    break;
                case 3:try {
                    queue.getQueue();
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
                    break;
                case 4:try{
                    queue.headQueue();
                }catch (Exception e){
                    System.out.println(e.getMessage());
                }
                    break;
                case 5:System.exit(0);
            }
        }
}

    /*
        判断是否满:(rear+1)% maxSize == front
        判断是否空: rear=front
        队列有效数据个数: (rear+maxSize-front)%maxSize
     */

    static class CircleQueue{
        private int maxSize;//数组的长度
        private int front;//队列头(第一个数据)
        private int rear;//队列尾部后一个
        private int[] arr;//存放数据,模拟队列


        //初始化队列
        public CircleQueue(int arrMaxSize){
            maxSize=arrMaxSize;
            arr=new int[maxSize];
            front=0;
            rear=0;
        }

        //判断队列是否满

        public boolean isFull(){
            return (rear+1)%maxSize==front;
        }

        //判断队列是否空
        public boolean isEmpty(){
            return front==rear;
        }

        //添加数据到队列
        public void addQueue(int n){
            //判断队列是否满了
            if(isFull()){
                System.out.println("队列满了呀,不能添加数据哦!");
                return;
            }
            //未满,则添加
            arr[rear]=n;
            //rear指针往后移
            rear=(rear+1)%maxSize;
        }

        //出队列
        public int getQueue(){
            //判断是否为空
            if (isEmpty()){
                //为空,则抛出异常
                throw new RuntimeException("队列是空的呀,不能取出数据!");
            }
            //若队列不为空
            int value=arr[front];
            //front指针后移
            front=(front+1)%maxSize;
            return value;
        }

        //显示队列全部数据
        public  void showQueue(){

            if (isEmpty()){
                System.out.println("队列是空的呀,没有数据!");
            }
            //若队列不为空,则遍历队列(数组)
            for (int i = front; i < (front+(rear+maxSize-front)%maxSize); i++) {
                System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
            }
        }


        //显示队列头数据
        public int headQueue(){
            if (isEmpty()){
                throw new RuntimeException("队列是空的呀,没有数据!");
            }
            return arr[front];
        }
    }
}

二、单链表

可以是不连续的空间地址

head:头结点(不放数据) 如何判断结束,当节点不指向下一个节点时为结束标志

2.1、添加节点

主要思路:

  • 我们要往链表里面添加节点,就要遍历链表(while循环)找到链表的最后一个节点,当找到最后一个节点时,将最后一个节点的next(也就是下一个节点的指向)指向新的节点这样就成功的将两个节点连接起来了。
  • 链表为空-条件:head.next=null

代码实现:不考虑编号

package LinkList;

public class SingleLinkListDeom {
    public static void main(String[] args) {
        //创建结点
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
        //创建链表
        SingleLinkList singleLinkList = new SingleLinkList();
        singleLinkList.add(hero1);
        singleLinkList.add(hero2);
        singleLinkList.add(hero3);
        singleLinkList.add(hero4);
        //显示
        singleLinkList.list();
    }

    //使用链表管理节点(英雄)
    static class SingleLinkList{

        //先初始化头结点,不存放具体的数据
        private  HeroNode head=new HeroNode(0,"","");

        //添加结点到单链表
        //当不考虑链表的顺序时,找到当前结点的最后结点,然后将当前结点的next指向新结点
        public void add(HeroNode heroNode){
            HeroNode temp=head;//因为头结点不能动,所以需要辅助遍历 temp
            //遍历链表,
            while (true){
                //找到最后的结点
                if(temp.next==null){
                    break;
                }
                //如果没有找到,将temp后移
                temp=temp.next;
            }
            //当退出循环时,temp就指向链表的最后,
            temp.next=heroNode;
        }

        //显示链表  遍历(需要辅助变量)
        public void list(){
            //先判断链表是否为空
            if(head.next==null){
                System.out.println("链表为空");
                return;
            }
            HeroNode temp=head.next;//因为头结点不能动,所以需要辅助变量
            while (true){
                //判断链表是否到最后
                if (temp==null){
                    break;
                }
                //输出结点信息
                System.out.println(temp);
                //将next后移
                temp=temp.next;
            }
        }


    }

    //节点对象
    static class HeroNode{
        public int no;
        public String name;
        public String niceName;
        public HeroNode next;//指向下一个节点

        public HeroNode(int no, String name, String niceName) {
            this.no = no;
            this.name = name;
            this.niceName = niceName;
        }

        @Override
        public String toString() {
            return "HeroNode{" +
                    "no=" + no +
                    ", name='" + name + '\'' +
                    ", niceName='" + niceName + '\'' +
                    '}';
        }
    }
}

考虑编号:

思路:首先定义一个临时的指针—temp,帮助我们找到要插入节点的位置,然后遍历链表(其实就是通过循环,不断地让temp指针往后指,直到找到符合的节点,就break(结束循环)),

那么结束循环的条件是啥呢?如下:

  1. 链表都遍历完了,没有符合条件的节点,指针指向最后一个节点,也就是:temp.next==null;
  2. 在遍历的时候,找到了符合的,也就是:temp.next.编号>插入节点.编号
  3. 在遍历的时候,发现已经存在了该编号。

当我们找到了要插入的位置时,只需要让插入节点的next指向temp节点的下一个节点(heroNode.next=temp.next;),然后将temp节点的next指向插入节点(temp.next=heroNode)。

//考虑编号
public void add2(HeroNode heroNode){
    //辅助指针 temp ,帮助找到添加的位置
    HeroNode temp=head;
    boolean flag=false;//添加的编号是否存在
    while (true){
        if (temp.next==null){//temp在链表的最后
            break;
        }
        if (temp.next.no>heroNode.no){//说明符合编号顺序
            break;//退出循环
        }
        else if (temp.next.no==heroNode.no){
            //说明要添加编号存在
            flag=true;
            break;
        }
        temp=temp.next;//往后移--相当于遍历当前链表,
    }
    //判断falg值
    if (flag){
        System.out.println("要插入的英雄编号已经存在"+heroNode.no);
    }else {
        //插入到链表
        heroNode.next=temp.next;
        temp.next=heroNode;
    }

}

2.2、根据编号修改

​ 思路:首先判断链表是否为空,为空还咋找?然后搞一个临时指针指向头节点的下一个节点,找到要修改的节点,那么咋找呢?就是遍历链表(其实就是通过循环,不断地让temp指针往后指,直到找到符合的节点,就break(结束循环)),循环结束条件:

  1. 链表都遍历完了,指针指向最后一个节点,也就是:temp==null;
    //根据编号修改
    public void update(HeroNode newHeroNode){
        //判断是否为空
        if (head.next==null){
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点
        HeroNode temp=head.next;
        boolean flag=false;//表示是否找到该节点
        while (true){
            if (temp==null){
                break;//已经遍历完链表
            }
            if (temp.no==newHeroNode.no){
                flag=true;
                break;
            }
            //指针往后移
            temp=temp.next;
        }
        if (flag){
            temp.name=newHeroNode.name;
            temp.niceName=newHeroNode.niceName;
        }else {
            System.out.println("没有找到该节点!"+newHeroNode.no);
        }
    }

}

2.3、根据编号删除节点

思路:

  1. 首先先找到要删除节点的上一个节点,我们用临时节点temp表示 条件:temp.next.no==编号;

  2. 然后直接让temp节点的下一个节点直接指向删除节点的下一个节点 条件:temp.next=temp.next.next

//根据指定编号删除节 点
public void delete(int no){
    if (head.next==null){
        System.out.println("链表为空");
        return;
    }
    //指定一个临时指针节点  
    HeroNode temp=head;
    boolean flag=false;
    while (true){
        if (temp.next==null){
            break;
        }
        if (temp.next.no==no){
            flag=true;
            break;
        }
        //指针往后移
        temp=temp.next;
    }
    if(flag){
        temp.next=temp.next.next;
    }else {
                System.out.println("没有该节点");
            }
}

获取单链表的有效节点个数(不计头结点) head-表示链表的头节点

思路:先判断链表不为空,定义一个变量来记录遍历链表的次数

//head-表示头节点   
public static int getLength(HeroNode head){
        if (head.next==null){
            System.out.println("链表为空");
            return 0;
        }
        int count=0;
        HeroNode temp=head.next;
//        while (true){
//            if (temp==null){
//                break;
//            }
//            temp=temp.next;
//            count++;
//        }
        while (temp!=null){
            count++;
            temp=temp.next;
        }
        return count;
    }

输出:System.out.println(getLength(singleLinkList.getHead()));

2.4、查找单链表的倒数第k个节点

思路:判断链表不为空,根据传过来的链表得到有效节点的个数(length),再判断传的k是否合法,根据length-k找到要查找的节点

//查找单链表的倒数第k个节点
public static HeroNode findLastNode(HeroNode head,int k){
    if (head.next==null){
        System.out.println("链表为空");
        return null;
    }
    //第一次遍历,得到有效节点个数
    int length = getLength(head);
    //第二次遍历到length-k位置,就是倒数第k的节点
    if (length<=0||k>length){
        return null;
    }
    HeroNode temp=head.next;//temp为第一个有效节点
    for (int i = 0; i < length-k; i++) {
        temp=temp.next;
    }
    return temp;
}

输出:System.out.println(findLastNode(singleLinkList.getHead(),2));

2.5、链表反转

思路:定义一个新的链表,头节点为reverseHead,准备一个空的next节点,遍历让其指向下一个节点,保存下来(因为当你取出一个节点时,原来的连接就断了,temp.next为空)。然后遍历链表,每遍历一个节点,就把这个节点放到新链表的最前面,最后遍历结束时,让原来链表头节点的下一个节点指向新链表头节点的下一个节点,也就是让head.next指向原来链表的最后一个节点(因为已经实现了反转)

//链表翻转
public static void reverseLinkList(HeroNode head){
    //链表为空  链表只有一个节点
    if (head.next==null||head.next.next==null){
        return;
    }
    //定义一个指针,遍历原来的链表
    HeroNode temp=head.next;
    //指向当前节点(temp)的下一个节点
    HeroNode next=null;
    HeroNode reverseHead=new HeroNode(0,"","");
    //遍历原来的链表,没遍历一个节点,就取出,放在新链表reverseHead的最前端
    while (temp!=null){
        next=temp.next;//先暂时保存当前节点的下一个节点,因为后面需要使用
        temp.next=reverseHead.next;
        reverseHead.next=temp;
        temp=next;//让temp后移
    }
    //将原来链表的head.next指向reverseHead.next
    head.next=reverseHead.next;
}

递归实现反转链表:

思路:假设链表箭头的方向从左往右,共有四个有效节点 1—>2—>3—>4

​ 先将链表递归到最后一个节点-HeroNode reverseHead = reverseLinkList2(head.next),

此时head.next表示最后一个节点-4,head表示倒数第二个节点-3,reverseHead 表示最后一个节点-4,这时最重要的反转来了:

head.next.next = head 由于递归:head 是变化的

第一次:让节点4的下一个节点指向节点3 == 相当于 4—>3 此时head是3

第二次:让节点3的下一个节点指向节点2 ==相当于4—>3—>2 此时head是2

第三次:让节点2的下一个节点指向节点1 ==相当于4—>3—>2—>1 此时head是1

//链表翻转--法二  递归
public static HeroNode reverseLinkList2(HeroNode head) {

    if (head == null || head.next == null) {
        return head;
    }
    // 遍历到链表尾部
    HeroNode reverseHead = reverseLinkList2(head.next);
    // 反转
    head.next.next = head;
    head.next = null;
    return reverseHead;
}

2.6、从尾到头打印链表,不改变原链表

//从尾到头打印链表,不改变原链表
public static void reversePrint(HeroNode head){
    if (head.next==null){
        return;//空链表
    }
    //创建一个栈,将各个节点压入栈
    Stack<HeroNode> stack = new Stack<>();
    HeroNode temp=head.next;
    while (temp!=null){
        stack.push(temp);
        temp=temp.next;
    }
    //打印栈里节点
    while (stack.size()>0){
        System.out.println(stack.pop());
    }
}

2.7、合并两个有序链表:递归方式

public static HeroNode twoLinkList(HeroNode heroNode1,HeroNode heroNode2){
    if (heroNode1==null) return heroNode2;
    if (heroNode2==null) return heroNode1;
    HeroNode temp;
    if (heroNode1.no<heroNode2.no){
        temp=heroNode1;
        temp.next=twoLinkList(heroNode1.next,heroNode2);
    }else {
        temp=heroNode2;
        temp.next=twoLinkList(heroNode1,heroNode2.next);
    }
    return temp;
}

打印:

HeroNode heroNode = toLinkList(singleLinkList.getHead(), singleLinkList2.getHead());
while (heroNode!=null){
    System.out.println(heroNode);
    heroNode=heroNode.next;
}

三、双向链表

  • 可以双向查找 next表示(指向下一个节点);pre表示(指向前一个节点)
static class LinkNode{
    public int id;
    public String name;
    public LinkNode next;
    public LinkNode pre;

    public LinkNode(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "LinkNode{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

3.1、遍历链表

//遍历链表
public void list(){
    if (head.next==null){
        System.out.println("链表为空");
        return;
    }
    LinkNode temp=head.next;//代表是当前节点
    while (true){
        if (temp==null){
            break;
        }
        System.out.println(temp);
        temp=temp.next;
    }
}

3.2、添加节点

思路:

  1. 遍历找到双向链表的最后一个节点,假如是temp
  2. 让temp的下一个节点指向新的节点,即 temp.next=newNode
  3. 让新节点的前一个节点指向temp,即 newNode.pre=temp
//添加节点
public void addNode(LinkNode newNode){
    LinkNode temp=head;
    while (true){
        if (temp.next==null){
            break;
        }
        temp=temp.next;
    }
    temp.next=newNode;
    newNode.pre=temp;
}
public static void main(String[] args) {
    LinkNode linkNode1 = new LinkNode(1,"懒羊羊");
    LinkNode linkNode2 = new LinkNode(2,"美羊羊");
    LinkNode linkNode3 = new LinkNode(3,"喜洋洋");
    LinkNode linkNode4 = new LinkNode(4,"小灰灰");

    DoubleLink link = new DoubleLink();
    //添加节点
    System.out.println("++++++++++没有根据编号添加++++++++++++");
    link.addNode(linkNode1);
    link.addNode(linkNode2);
    link.addNode(linkNode4);
    link.addNode(linkNode3);
    link.list();
}

在这里插入图片描述

3.3、添加节点 根据编号顺序

思路:temp 表示要插入节点的前一个节点

  1. newNode.next=temp.next—>让新节点的下一个节点 指向 temp的下一个节点 这样就让新节点插入到了temp的后面
  2. temp.next.pre=newNode—>让temp的下一个节点的前一个节点指向新节点 这样就让新节点链接到了temp的下一个节点的前面
 //添加节点 按照顺序
public  void addNodeById(LinkNode newNode){
     LinkNode temp=head;
     boolean flag=false;//判断要添加的编号是否已经存在
     while (true){
         if (temp.next==null){
             break;
         }
         if (temp.next.id>newNode.id){
             break;
         }
         if (temp.next.id==newNode.id){
             flag=true;
             break;
         }
         temp=temp.next;
     }
     if (flag){
         System.out.println("该编号已经存在");
     }else {
         if (temp.next!=null){
             newNode.next=temp.next;//让新节点的下一个节点  指向  temp的下一个节点  这样就让新节点插入到了temp的后面
             temp.next.pre=newNode;//让temp的下一个节点的前一个节点指向新节点  这样就让新节点链接到了temp的下一个节点的前面
         }
         temp.next=newNode;
         newNode.pre=temp;
     }
}
System.out.println("++++++++++++根据编号添加++++++++++++");
link.addNodeById(linkNode1);
link.addNodeById(linkNode4);
link.addNodeById(linkNode2);
link.addNodeById(linkNode3);
link.list();

在这里插入图片描述

3.4、修改节点

思路:同单链表相同

  1. 遍历链表,找到要修改的节点
  2. 然后直接将原来节点的值覆盖即可
 //修改节点
public void updateNode(LinkNode newNode){
     if (head.next==null){
         System.out.println("链表为空");
         return;
     }
     LinkNode temp=head.next;
     boolean flag=false;
     while (true){
         if (temp==null){
             break;
         }
         if (temp.id==newNode.id){
             flag=true;
             break;
         }
         temp=temp.next;
     }
     if (flag){
         temp.name=newNode.name;
     }else {
         System.out.println("没有该节点");
     }
}

3.5、删除节点

思路:

  1. 还是先要遍历,根据编号找到要删除的节点 ,此时temp就是要删除的节点(LinkNode temp = head.next),单链表找的是要删除节点的前一个节点(LinkNode temp=head
  2. 找到之后,由于是双链表,所以让当前节点(也就是要删除的节点)的前一个节点的下一个节点指向当前节点的下一个节点(temp.pre.next=temp.next),然后让当前节点的下一个节点的前一个节点指向当前节点的前一个节点(temp.next.pre=temp.pre)。这样就让 要删除节点的 前后节点链接起来了。
  3. 注意:当要删除的节点是最后一个节点时,temp.next为null会出现空指针异常, 所以要判断temp.next不为空。
//删除节点
public void deleteNode(int id){
     if (head.next==null){
         System.out.println("链表为空");
         return;
     }
     LinkNode temp = head.next;
     boolean flag=false;
     while (true){
         if (temp==null){
             break;
         }
         if (temp.id==id){
             flag=true;
             break;
         }
         temp=temp.next;
     }
     if (flag){
         temp.pre.next=temp.next;
         //temp.next.pre=temp.pre;//注意这里 temp.next可能为空,因为可能要删除的是最后一个节点 就会出现空指针异常
         if (temp.next!=null){
             temp.next.pre=temp.pre;
         }
     }else {
         System.out.println("没有该节点");
     }
}

LinkNode newNode = new LinkNode(4,"灰太狼");
//修改节点
link.updateNode(newNode);
System.out.println();
System.out.println("++++++++ 修改节点后 +++++++++++++");
link.list();

//删除节点
link.deleteNode(4);
System.out.println();
System.out.println("+++++++ 删除节点后 +++++++++++++");
link.list();

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

揽星河吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值