【自学笔记】尚硅谷数据结构与算法Chapter 3 链表

Chapter 3 链表

3.1 链表

3.1.1 链表介绍

  • 链表以结点方式存储,存储空间不一定连续
  • 每个结点包含数据域和指针域
  • 每个结点不一定是连续存储
  • 链表有带头结点和不带头结点的,根据需求确定使用哪一种

3.1.2 链表CRUD

1、按输入顺序建立一个学生链表

/**
 * The type Single linked list.
 * @author ybs
 */
public class SingleLinkedListTest {
    public static void main(String[] args) {
        StudentNode s1 = new StudentNode(1, "jack", "c1");
        StudentNode s2 = new StudentNode(2, "york", "c1");
        StudentNode s3 = new StudentNode(3, "peter", "c1");
        StudentNode s4 = new StudentNode(4, "marry", "c1");

        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.addNode(s1);
        singleLinkedList.addNode(s2);
        singleLinkedList.addNode(s3);
        singleLinkedList.addNode(s4);

        singleLinkedList.listLinkedList();
    }
}

class SingleLinkedList{
    /**
     * head 头结点
     */
    public StudentNode head = new StudentNode(0, "", "");

    public void addNode(StudentNode student){

        StudentNode temp = head;
        //找到最后一个结点
        while(true){
            if(temp.next==null) {
                break;
            }
            temp = temp.next;
        }
        temp.next=student;
    }

    public void listLinkedList(){
        if(head.next==null){
            System.out.println("空链表");
            return;
        }

        StudentNode temp = head.next;
        while(true){
            if(temp==null){
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }

}

class StudentNode{
    public int stuNo;
    public String stuName;
    public String stuClass;
    public StudentNode next;

    public StudentNode(int stuNo, String stuName, String stuClass) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.stuClass = stuClass;
    }

    @Override
    public String toString() {
        return "Student{" +
                "stuNo=" + stuNo +
                ", stuName='" + stuName + '\'' +
                ", stuClass='" + stuClass + '\'' +
                '}';
    }
}

2、按照编号顺序建立一个学生表

/**
 * The type Single linked list.
 * @author ybs
 */
public class SingleLinkedListTest {
    public static void main(String[] args) {
        StudentNode s1 = new StudentNode(1, "jack", "c1");
        StudentNode s2 = new StudentNode(2, "york", "c1");
        StudentNode s3 = new StudentNode(3, "peter", "c1");
        StudentNode s4 = new StudentNode(4, "marry", "c1");

        SingleLinkedList singleLinkedList = new SingleLinkedList();
/*        singleLinkedList.addNode(s1);
        singleLinkedList.addNode(s2);
        singleLinkedList.addNode(s3);
        singleLinkedList.addNode(s4);*/

        singleLinkedList.addNodeByOrder(s3);
        singleLinkedList.addNodeByOrder(s1);
        singleLinkedList.addNodeByOrder(s2);
        singleLinkedList.addNodeByOrder(s4);
        singleLinkedList.addNodeByOrder(s4);

        singleLinkedList.listLinkedList();
    }
}

class SingleLinkedList{
    /*其他同1*/

    //按照序号加入链表
    public void addNodeByOrder(StudentNode student){
        StudentNode temp = head;
        boolean flag = false;//判断是否有重复的序号
        //找到插入位置
        while(true){
            if(temp.next==null){
                break;
            }
            if(temp.next.stuNo > student.stuNo){
                break;
            }else if(temp.next.stuNo == student.stuNo){
                flag=true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            System.out.printf("重复序号%d,无法加入\n",student.stuNo);
        }else{
            student.next = temp.next;
            temp.next = student;
        }
    }

}

class StudentNode{
    /*同1*/
}

3、修改链表

/**
 * The type Single linked list.
 * @author ybs
 */
public class SingleLinkedListTest {
    public static void main(String[] args) {
        StudentNode s1 = new StudentNode(1, "jack", "c1");
        StudentNode s2 = new StudentNode(2, "york", "c1");
        StudentNode s3 = new StudentNode(3, "peter", "c1");
        StudentNode s4 = new StudentNode(4, "marry", "c1");

        SingleLinkedList singleLinkedList = new SingleLinkedList();
/*        singleLinkedList.addNode(s1);
        singleLinkedList.addNode(s2);
        singleLinkedList.addNode(s3);
        singleLinkedList.addNode(s4);*/

        singleLinkedList.addNodeByOrder(s3);
        singleLinkedList.addNodeByOrder(s1);
        singleLinkedList.addNodeByOrder(s2);
        singleLinkedList.addNodeByOrder(s4);
        singleLinkedList.addNodeByOrder(s4);
		singleLinkedList.listLinkedList();
        singleLinkedList.updateNode(new StudentNode(3, "sam", "c2"));
        singleLinkedList.listLinkedList();
    }
}

class SingleLinkedList{
    /*其他同1*/

    //修改结点
    //根据要修改的结点newStudent的stuNo编号查找修改
    public void updateNode(StudentNode newStudent){
        if(head.next==null){
            System.out.println("空链表");
            return;
        }
        StudentNode temp = head.next;
        boolean flag = false;//判断是否找到序号
        while(true){
            if(temp==null){
                break;
            }
            if(temp.stuNo== newStudent.stuNo){
                flag=true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.stuName = newStudent.stuName;
            temp.stuClass = newStudent.stuClass;
        }else{
            System.out.printf("未找到序号%d的学生",newStudent.stuNo);
        }
    }

}

class StudentNode{
    /*同1*/
}

3.1.3 单链表题目

1、求单链表的有效结点个数

//获取单链表的长度
public static int getLength(StudentNode head){
    if(head.next == null){
        return 0;
    }
    int length = 0;
    StudentNode temp = head.next;
    while(temp!=null){
        length++;
        temp=temp.next;
    }
    return length;
}

2、查找单链表倒数第k个结点

//获取单链表倒数第index个元素
//1.先得到单链表目前的长度size  2.从头遍历(size-index)个
public static StudentNode getLastIndexNode(StudentNode head, int index){
    if(head.next==null || index <= 0 || index > getLength(head)){
        return null;
    }
    int size = getLength(head);
    StudentNode temp = head.next;
    for(int i=0;i<(size-index);++i){
        temp = temp.next;
    }
    return temp;
}

3、单链表反转

//反转单链表
//1.设置一个新的头结点reverseHead    2.遍历链表,每次将结点放到reverseHead的最前端  3.将原来的头结点指向新的头结点的下一个结点
public static void reverseLinkedList(StudentNode head){
    //如果为空或者只有一个结点就直接返回
    if(head.next==null||head.next.next==null){
        return;
    }
    StudentNode temp = head.next;//循环指针
    StudentNode next = null;//指向当前循环下一个的指针
    StudentNode reverseHead = new StudentNode(0, "", "");//新的头结点
    while(temp!=null){
        next = temp.next;
        temp.next = reverseHead.next;
        reverseHead.next = temp;
        temp = next;
    }
    head.next = reverseHead.next;
}

4、从尾到头打印单链表

//从尾到头打印单链表
public void reverseListLinkedList(){
    if(head.next==null){
        return;
    }
    Stack<StudentNode> stack = new Stack<>();
    StudentNode temp = head.next;
    while(temp!=null){
        stack.push(temp);
        temp = temp.next;
    }
    while(stack.size()>0){
        System.out.println(stack.pop());
    }
}

5、合并两个有序单链表,合并之后依然有序

//合并两个有序单链表,合并依然有序
public static SingleLinkedList mergeLinkedList(SingleLinkedList sglList1, SingleLinkedList sglList2){
    SingleLinkedList singleLinkedList = new SingleLinkedList();
    StudentNode list1 = sglList1.head;
    StudentNode list2 = sglList2.head;
    StudentNode newList = new StudentNode(0, "", "");
    if(list1.next==null){
        newList.next = list2.next;
        singleLinkedList.head = newList;
        return singleLinkedList;
    }else if(list2.next==null){
        newList.next  = list1.next;
        singleLinkedList.head = newList;
        return singleLinkedList;
    }
    StudentNode temp1 = list1.next;
    StudentNode temp2 = list2.next;
    StudentNode tempNewList = newList;
    while (temp1!=null && temp2!=null){
        if(temp1.stuNo< temp2.stuNo){
            tempNewList.next=temp1;
            tempNewList = tempNewList.next;
            temp1 = temp1.next;
        }else{
            tempNewList.next=temp2;
            tempNewList = tempNewList.next;
            temp2 = temp2.next;
        }
    }
    if (temp1==null){
        tempNewList.next = temp2;
    }else{
        tempNewList.next = temp1;
    }
    singleLinkedList.head = newList;
    return singleLinkedList;
}

3.2 双向链表

3.2.1 双向链表介绍

  • 单链表只能往一个方向查找,双向链表可以向前向后查找
  • 单链表的结点不能自我删除,双链表的结点可以

3.2.2 双向链表CRUD

public class DoubleLinkedListTest {
    public static void main(String[] args) {
        StudentNode2 s1 = new StudentNode2(1, "jack", "c1");
        StudentNode2 s2 = new StudentNode2(2, "york", "c1");
        StudentNode2 s3 = new StudentNode2(3, "peter", "c1");
        StudentNode2 s4 = new StudentNode2(4, "marry", "c1");
        StudentNode2 s5 = new StudentNode2(5, "sam", "c2");

        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.addNode(s1);
        doubleLinkedList.addNode(s2);
        doubleLinkedList.addNode(s3);
        doubleLinkedList.addNode(s4);
        doubleLinkedList.addNode(s5);

        //doubleLinkedList.updateNode(new StudentNode2(3, "peter", "c2"));
        //doubleLinkedList.deleteNode(3);
        doubleLinkedList.listLinkedList();

    }
}

class DoubleLinkedList{
    /**
     * head 头结点
     */
    public StudentNode2 head = new StudentNode2(0, "", "");

    public void addNode(StudentNode2 student){

        StudentNode2 temp = head;
        //找到最后一个结点
        while(true){
            if(temp.next==null) {
                break;
            }
            temp = temp.next;
        }
        temp.next=student;
        student.previou = temp;
    }

    public void listLinkedList(){
        if(head.next==null){
            System.out.println("空链表\n");
            return;
        }

        StudentNode2 temp = head.next;
        while(true){
            if(temp==null){
                break;
            }
            System.out.println(temp);
            temp = temp.next;
        }
    }

    //删除结点
    //根据编号删除结点
    public void deleteNode(int stuNo){
        StudentNode2 temp = head.next;
        boolean flag = false;//判断是否找到该结点
        while(true){
            if(temp==null){
                break;
            }
            if(temp.stuNo==stuNo){
                flag=true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.next.previou = temp.previou;
            temp.previou.next = temp.next;
        }else{
            System.out.printf("未找到编号为%d的结点",stuNo);
        }
    }

    //修改结点
    //根据要修改的结点newStudent的stuNo编号查找修改
    public void updateNode(StudentNode2 newStudent){
        if(head.next==null){
            System.out.println("空链表");
            return;
        }
        StudentNode2 temp = head.next;
        boolean flag = false;//判断是否找到序号
        while(true){
            if(temp==null){
                break;
            }
            if(temp.stuNo== newStudent.stuNo){
                flag=true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.stuName = newStudent.stuName;
            temp.stuClass = newStudent.stuClass;
        }else{
            System.out.printf("未找到序号%d的学生",newStudent.stuNo);
        }
    }
}

class StudentNode2 {
    public int stuNo;
    public String stuName;
    public String stuClass;
    public StudentNode2 next;
    public StudentNode2 previou;

    public StudentNode2(int stuNo, String stuName, String stuClass) {
        this.stuNo = stuNo;
        this.stuName = stuName;
        this.stuClass = stuClass;
    }

    @Override
    public String toString() {
        return "Student{" +
                "stuNo=" + stuNo +
                ", stuName='" + stuName + '\'' +
                ", stuClass='" + stuClass + '\'' +
                '}';
    }
}

3.3 循环链表和约瑟夫问题

3.3.1 单向环形链表的应用场景

Josephu(约瑟夫、约瑟夫环)问题

image-20211203145828635

3.3.2 单向环形链表的构建与遍历

1、构建思路

先创建一个first结点,并形成环形,每增加一个新的结点,就把该结点加入已有的环形链表中

2、遍历思路

先让一个辅助指针cur指向first,然后通过while遍历环形链表,当cur.next=first时结束

3、实现

public class JosephuProblemTest {
    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addGirl(5);
        circleSingleLinkedList.listGirls();
    }
}

class CircleSingleLinkedList{
    public Girl first = null;

    public CircleSingleLinkedList(){}

    public void addGirl(int num){
        if (num<1){
            System.out.println("num不符合");
            return;
        }
        Girl curGirl = null;
        for(int i=1;i<=num;++i){
            Girl girl = new Girl(i);
            if (i==1){
                first = girl;
                curGirl = first;
                girl.setNext(first);
            }else{
                curGirl.setNext(girl);
                girl.setNext(first);
                curGirl = girl;
            }
        }
    }

    public void listGirls(){
        if (first==null){
            System.out.println("空表");
            return;
        }
        Girl curGirl = first;
        while(true){
            System.out.println("Girl "+curGirl.getId());
            if(curGirl.getNext()==first){
                break;
            }
            curGirl = curGirl.getNext();
        }
    }
}

class Girl{
    private int id;
    private Girl next;

    public Girl(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Girl getNext() {
        return next;
    }

    public void setNext(Girl next) {
        this.next = next;
    }
}

3.3.3 约瑟夫问题实现

思路:n 一共有几个人; k 表示从第k个人开始报数; m 表示每次数几个数

  1. 需要创建一个辅助指针helper,事先应该指向环形链表的最后一个结点
  2. 报数前,让 first 和 helper 同时移动 k-1 次
  3. 报数时,让 first 和 helper 同时移动 m-1 次
  4. 此时 first 指向的结点出圈
/**
     * 约瑟夫问题实现
     *
     * @param startId  表示从第几个开始数数
     * @param countNum 表示每次数到几出圈
     * @param sumNum   表示最初有多少小孩
     */
public void josephuCountGirl(int startId,int countNum,int sumNum){
    if (first==null || startId < 1 || startId > sumNum){
        System.out.println("链表或输入异常");
        return;
    }
    Girl helper = first;
    //将helper移动到最后一个结点
    while(true){
        if(helper.getNext()==first){
            break;
        }
        helper = helper.getNext();
    }
    //将first移动到起始结点,helper移动到first的前一个结点
    for(int i=0;i<startId-1;++i){
        first = first.getNext();
        helper = helper.getNext();
    }
    //数数,出圈
    while(true){
        //first==helper时,说明圈内只剩下一个结点,跳出
        if (first==helper){
            break;
        }
        //数数
        for (int i=0;i<countNum-1;++i){
            first = first.getNext();
            helper = helper.getNext();
        }
        System.out.printf("%d出圈\n",first.getId());
        first = first.getNext();//first移动到出圈结点的下一个结点
        helper.setNext(first);//helper.next指向first,出圈结点被移出链表
    }
    System.out.printf("最后剩下%d\n",first.getId());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值