数据结构(Java版2022-10-29)
第一章:算法介绍
数据结构与算法面试题“
一.字符串匹配问题:有一个字符串str1="计算机科学与技术学院欢迎您!" 和另一个字符串 str2="计算机科学与技术学院",现在要判断str1是否含有str2如果存在,就返回第一次出现的位置,如果没有就返回-12.要求使用最快的速度来完成3.你的思路是什么?方法:暴力匹配(无脑算法) KMP算法(部分匹配表)二.汉诺塔算法:将A塔的所有圆盘移动到C塔,并且规定 在2小圆盘上不能放大圆盘 3在三根柱子之间一次只能一次移动一个圆盘使用分治算法
三.八皇后问题:任意两个皇后都不能处于同一行,同一行或同一斜线上,问有多少摆法使用到回溯算法
四:马踏棋盘算法算法重要性:算法是程序的灵魂,优秀的程序可以在海量数据计算时,,依然保持高速计算
一般来说:程序会使用内存计算框架和缓存技术来优化程序 在深入的思考
第二章:数据结构与算法概述(链表,队列)
数据结构和算法的关系:数据(data)结构(structure)是一门研究组织数据方式的学科,有了编程语言也就有了数据结构
程序=数据结构+算法数据结构包括;线性结构和非线性结构
线性结构:1.线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系
2.线性结构有两种不同的存储方式:顺序存储方式和链式存储方式 顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
3.链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
4.线性结构常见的有:数组,队列, 链表, 栈。
非线性结构:包括 二维数组, 多维数组, 广义表,树结构, 图结构。稀疏数组和队列:需求:在编写五子棋盘程序中 有存盘和续上盘的功能因为该二维数组的很多值是默认为0,一次记录了很多没有意义的数据-->稀疏数组稀疏数组介绍:当一个数组中大部分元素为0,或者同一个值的数组时 可以使用稀疏数组来保存该数组稀疏数组的处理方法是:1.记录数组一共有几行几列 有多少个不同的值
把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模稀疏数组的转换分析:使用稀疏数组来存盘
思路:1.首先要去遍历原始的二维数组得到有效数据的个数sum
2.根据sum就可以创建稀疏数组Arr int[sum+1][3]
3将二维数组的有效数据存入到稀疏数组
稀疏数组转原始的二维数组的思路:1.先读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如上面的chessArr=int[11][11]
2.在读取稀疏数组的后几行数据,并赋值给原始的二维数组即可代码实现:package 数据结构与算法;public class SparseArray {public static void main(String[] args) {
// 1.创建一个原始的二维数组 11*11
// 0:表示没有棋子 1表示黑子 2表示蓝字int chessArr1[][]=new int[11][11];chessArr1[1][1]=1;chessArr1[2][3]=2;
// 输出原始的二维数组System.out.println("原始的二维数组为:");for (int[] row:chessArr1){for (int data:row) {System.out.printf("%d\t",data);}System.out.println();}//将二维数组转换为稀疏数组
// 1.先遍历二维数组,得到非0数据的个数int sum=0;for (int i = 0; i < 11; i++) {for (int j = 0; j <11 ; j++) {if (chessArr1[i][j] != 0) {sum++;}}}System.out.println("sum"+sum);// 2. 创建对应的稀疏数组int sparseArr[][]=new int[sum+1][3];
// 给稀疏数组赋值sparseArr[0][0]=11;sparseArr[0][1]=11;sparseArr[0][2]=sum;// 3.遍历二维数组,将非0的值存放到sparseArr中int count=0; //用来记录是第几个非0数据for (int i = 0; i < 11; i++) {for (int j = 0; j <11 ; j++) {if (chessArr1[i][j] != 0) {count++;sparseArr[count][0]=i;sparseArr[count][1]=j;sparseArr[count][2]=chessArr1[i][j];}}}
// 输出稀疏数组的形式System.out.println();System.out.println("得到的稀疏数组为");for (int i = 0; i < sparseArr.length; i++) {System.out.printf("%d\t%d\t%d\t\n",sparseArr[i][0],sparseArr[i][1],sparseArr[i][2]);}System.out.println();//将稀疏数组恢复为原始的二维数组// 第一步:先读取稀疏数组的第一行 根据第一行的数据,创建原始的二维数组int chessArr2[][]=new int[sparseArr[0][0]][sparseArr[0][1]];
// 第二步:把读取到的数据赋值给数组2 从第二行开始for (int i = 2; i < sparseArr.length; i++) {chessArr2[sparseArr[i][0]][sparseArr[i][1]]=sparseArr[i][2];}// 恢复后的二维数组System.out.println();System.out.println();for (int[] row:chessArr2){for (int data:row) {System.out.printf("%d\t",data);}System.out.println();}}// 要求:将以上数据存入使用io流
}
队列:队列是一个有序列表,可以用数组或链表来实现遵循先入先出原则 即:先存入队列的数据,要先取出 后存入的要后取出因为队列的输入输出分别是从前后端来处理。因此需要两个变量front及rear分别记录前后端的下标 font会随着数据输出二改变 二rear则是随着数据输入而改变队列:
数组模拟队列:当我们将数据存入队列时称为 addQueue ,addQueue处理需要两个步骤1.将尾指针往后移 :rear+1 当font==rear[空]2.若尾指针rear小于队列的最大下标maxsize-1,则将数据存入rear所指的数组元素中 否则则无法存入数据 rear==maxsize-1【队列满】代码实现:package 数据结构与算法.ArrayQueue;import java.util.Scanner;public class ArrayQueue {public static void main(String[] args) {// 测试一下队列
// 创建一个队列ArrayQ arrayQ=new ArrayQ(3);char key;//接受用户输入Scanner scanner = new Scanner(System.in);boolean loop=true;
//输出一个菜单while (loop){System.out.println("s(show):显示队列");System.out.println("e(exit):退出队列");System.out.println("a(add):添加数据到队列");System.out.println("g(get):从队列列名取出数据");System.out.println("h(head):查看队列头丶数据");key=scanner.next().charAt(0);//接受一个字符switch (key){case 's':arrayQ.showQueue();break;case 'a':System.out.println("输出一个数");int value=scanner.nextInt();arrayQ.addQueue(value);break;case 'g'://取出数据try {int res=arrayQ.getQueue();System.out.println("取出的数据"+res);}catch (Exception e){System.out.println(e.getMessage());}break;case 'h'://查看队列头的数据是什么try {int res=arrayQ.headQueue();System.out.println("队列头的数据是"+res);}catch (Exception e){System.out.println(e.getMessage());}break;case 'e'://退出scanner.close();loop=false;default:break;}}System.out.println("程序退出!");}// 使用数组模拟队列-编写一个array类static class ArrayQ{private int maxSize;//表示数组的最大容量private int front; //对列头private int rear;//对列的尾部private int[] arr;//该数组用来存放数据,模拟队列// 1.创建队列的构造器public ArrayQ(int arrMaxsize){maxSize=arrMaxsize;// 初始化实例化对象arr=new int[maxSize];front =-1;//指向队列的头部,分析出front是指向队列头的前一个位置rear=-1;//指向队列的尾部的数据(就是队列最后一个数据)}// 判断队列是否满:public boolean isFull(){return rear==maxSize-1;//满足这个条件就说明队列已经满了}
// 判断队列是否为空public boolean isEmpty(){return rear==front;//满足这个条件就说明队列为空}// 添加数据到队列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("队列是空的,没有数据");
return;}
// 开始遍历队列数据for (int i = 0; i < arr.length; i++) {System.out.printf("arr[%d]=%d\n",i,arr[i]);}}// 显示队列的头数据,注意表示取出数据public int headQueue(){
// 首先要判断是不是空队列if (isEmpty()){System.out.println();
throw new RuntimeException("队列空,没有数据");}return arr[front+1];//指针往前移动}}}
问题分析:数组模拟环形队列:对前面的数组模拟队列的优化,充分利用数组队列小结:队列是一个有序列表,可以用数组或是链表来实现
遵循先入先出的原则,即:先入队列的数据要先取出,后存入的数据要后取出数组模拟队列的思路:
队列本身就是一个有序列表 若使用数组的结构来存储队列的数据,则队列数组的声明如下:其中maxsize是该队列的最大容量
因为队列的输出,输入分别是从前后端来处理,因此需要两个变量front即rear分别记录前后端的下标 front会随着数据的输出二改变 而rear则是随着数据输入而改变目前这个数组使用一次就不能使用,没有达到复用的效果
将这个·数组使用算法 改进成一个环形的数组 使用取模运算来实现数组模拟环形队列: 思路如下:
1·front变量的含义做一个调整:front就指向队列的第一个元素,也就是arr[front]就是队列的第一个元素front的初始值=0;
2.rear变量的含义做一个调整,rear指向队列的最后一个元素的后一个位置 因为希望空出一个空间为约定rear的初始值=0;
3.当队列满时 条件是[rear+1]%maxsize=front【满】
4.对队列为空的条件:rear==front空
5.当我们这样分析后;队列中有效的数据的个数为:(rear+maxsize-front)%maxsize在原来的基础队列做修改可以得到环形队列:
代码如下:package 数据结构与算法.CircleArray;import 数据结构与算法.ArrayQueue.ArrayQueue;import java.util.Scanner;public class CirlcleArray {public static void main(String[] args) {System.out.println("数组模拟环形队列:");// 测试一下队列
// 创建一个队列CircleArray queue=new CircleArray(3);char key;//接受用户输入Scanner scanner = new Scanner(System.in);boolean loop=true;
//输出一个菜单while (loop){System.out.println("s(show):显示队列");System.out.println("e(exit):退出队列");System.out.println("a(add):添加数据到队列");System.out.println("g(get):从队列列名取出数据");System.out.println("h(head):查看队列头丶数据");key=scanner.next().charAt(0);//接受一个字符switch (key){case 's':queue.showQueue();break;case 'a':System.out.println("输出一个数");int value=scanner.nextInt();queue.addQueue(value);break;case 'g'://取出数据try {int res=queue.getQueue();System.out.println("取出的数据"+res);}catch (Exception e){System.out.println(e.getMessage());}break;case 'h'://查看队列头的数据是什么try {int res=queue.headQueue();System.out.println("队列头的数据是"+res);}catch (Exception e){System.out.println(e.getMessage());}break;case 'e'://退出scanner.close();loop=false;default:break;}}System.out.println("程序退出!");}static class CircleArray{private int maxSize;//表示数组的最大容量private int front; //front变量的含义做一个调整:front就指向队列的第一个元素,也就是arr[front]就是队列的第一个元素
// front的初始值=0;private int rear;//rear变量的含义做一个调整,rear指向队列的最后一个元素的后一个位置 因为希望空出一个空间为约定
// rear的初始值=0;private int[] arr;//该数组用来存放数据,模拟队列public CircleArray(int arrMaxSize){maxSize=arrMaxSize;// 初始化实例化对象arr=new int[maxSize];}//判断队列是否满public boolean isFull(){return (rear+1)%maxSize==front;}
// 判断队列是否为空
public boolean isEmpty(){return rear==front;//满足这个条件就说明队列为空
}//添加数据到队列
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("队列为空不能取出数据");}
// 这里需要分析出,front是指向队列的第一个元素
// 1.先把front对应的值保存到一个临时变量
// 2.将front后移
// 34.将临时保存的变量返回int value=arr[front];front= (front+1)%maxSize;//让front后移,考虑取模return value;//出队列}// 显示队列中的所有数据public void showQueue(){
// 就是遍历,遍历数组
// 首先要判断队列是否为空,然后才能遍历if (isEmpty()){System.out.println("队列是空的,没有数据");return;}
// 从front开始遍历 遍历多少个元素
//
// 开始遍历队列数据for (int i = front; i < front+size(); i++) {System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);}}// 求出当前队列有效数据的个数public int size(){
// rear=2return (rear+maxSize-front)%maxSize;}// 显示队列的头数据,注意表示取出数据public int headQueue(){
// 首先要判断是不是空队列if (isEmpty()){System.out.println();throw new RuntimeException("队列空,没有数据");}return arr[front];//指针往前移动}}}链表:(Linked List):链表是有序列表
链表是以节点的方式来存储每个节点包含data域,next域。指向下一个节点其特点:链表的各个节点不一定是连续存储链表分带头节点的链表和没有头节点的链表。根据实际的需求来确定单链表介绍:
使用一个带head头节点的单向链表实现增删改查1.需求:完成对英雄人物的增删改查操作 注:删除和修改 查找可以考虑学员独立完成 2.第一种:在添加英雄时 直接添加到链表的尾部3.第二种:在添加英雄时,根据排名将英雄插入到指定位置单向链表的示例:[ head节点:1.不存放具体的数据 2.作用:就是表示单链表的头 ]
class HeroNode(int no ; String name; String nickName; HeroNode next)思路:1.先创建一个head节点,作用就是表示单链表的头
2.后面我们每添加一个节点 就直接加入到链表的最后遍历(如果有这个排名,则添加失败,并给出提示)、
package 数据结构与算法.LinkedList;public class SingLeLinkedList {public static void main(String[] args) {
//开始测试以下代码;// 1.先创建节点HeroNode hero1 = new HeroNode(1, "吴邪", "小三爷");HeroNode hero2 = new HeroNode(2, "解雨臣", "小花");HeroNode hero3 = new HeroNode(3, "张起灵", "小哥");HeroNode hero4 = new HeroNode(4, "黑眼睛", "黑瞎子");// 2.创建链表SingLeLinkedListTest singLeLinkedListTest = new SingLeLinkedListTest();
// 3. 加入人物
// singLeLinkedListTest.add(hero1);
// singLeLinkedListTest.add(hero2);
// singLeLinkedListTest.add(hero3);
// singLeLinkedListTest.add(hero4);// 现在按照编号加入:singLeLinkedListTest.addByOrder(hero1);singLeLinkedListTest.addByOrder(hero4);singLeLinkedListTest.addByOrder(hero3);singLeLinkedListTest.addByOrder(hero2);// 调用一把:singLeLinkedListTest.list();}
// 1.定义一个HereNode 每个HearNode 对象就是一个节点// 定义StringLeLinkedList来管理人物
static class SingLeLinkedListTest{
// 先初始化一个头结点,一般头节点不要乱动private HeroNode head=new HeroNode(0,"","");
// 添加节点到单向链表public void add(HeroNode heroNode){// 思路:当不考虑这个编号的顺序时,找到当前链表的最后节点 2,将最后这个节点的next 指向新的节点// 因为head 头节点不能动,一次需要一个辅助指针tempHeroNode temp=head;
// 遍历链表:找到最后while (true){// 找到链表的最后if (temp.next==null){//满足这个条件 已经找到了break;}// 如果没有找到最后temp=temp.next;//就将temp后移}//当退出这个循环的时,temp指向了链表的最后temp.next=heroNode;//将节点指向新的节点,赋值}
//开始书写第二轮需求public void addByOrder(HeroNode heroNode){
// 因为头节点不能动 因此我们仍然通过一个辅助指针来帮助找到添加的位置// 因为单链表 因为我们找到temp是位于添加位置的前一个节点,否则就插入不了数据HeroNode temp=head;boolean flag=false;// flag标志添加的编号是否存在 默认为FALSEwhile (true){if (temp.next==null) //这个条件一旦满足,就说明temp已经在链表的最后{break;//这个时候不管有没有找到,都要退出循环}if (temp.next.no> head.no){//位置找到 就在temp的后面插入break;}else if(temp.next.no==heroNode.no){//这个条件满足就说明了heroNode的编号已经存在flag=true;//说明编号存在break;}temp=temp.next;//后移,变量当前链表}
// 现在要判断flag的值if (flag){//不能添加,说明编号存在System.out.println("准备插入的英雄的已经存在"+heroNode.no);}else {// 插入到链表中,temp的后面heroNode.next=temp.next;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 nickname;public HeroNode next;//指向下一个节点
// 书写一个构造器public HeroNode(int no,String name,String nickname){this.no=no;this.name=name;this.nickname=nickname;}
// 为了显示方法,我们暂时重写toString
public String toString(){return "HeroNode [no="+no+",name="+name+",nickname="+nickname+"]";}
//需要按照编号的顺序添加:1.首先找到新添加的节点的位置,是通过辅助指针 2。让新的节点.next=temp.next
// 3.让temp.next=新的节点}}package 数据结构与算法.LinkedList;public class SingLeLinkedList {public static void main(String[] args) {
//开始测试以下代码;// 1.先创建节点HeroNode hero1 = new HeroNode(1, "吴邪", "小三爷");HeroNode hero2 = new HeroNode(2, "解雨臣", "小花");HeroNode hero3 = new HeroNode(3, "张起灵", "小哥");HeroNode hero4 = new HeroNode(4, "黑眼睛", "黑瞎子");// 2.创建链表SingLeLinkedListTest singLeLinkedListTest = new SingLeLinkedListTest();
// 3. 加入人物
// singLeLinkedListTest.add(hero1);
// singLeLinkedListTest.add(hero2);
// singLeLinkedListTest.add(hero3);
// singLeLinkedListTest.add(hero4);// 现在按照编号加入:singLeLinkedListTest.addByOrder(hero1);singLeLinkedListTest.addByOrder(hero4);singLeLinkedListTest.addByOrder(hero3);singLeLinkedListTest.addByOrder(hero2);// 修改前:System.out.println("修改前");singLeLinkedListTest.list();//测试修改节点的代码HeroNode newHeroNode=new HeroNode(2,"花","大宝贝");singLeLinkedListTest.update(newHeroNode);// 调用一把:System.out.println("修改后:");singLeLinkedListTest.list();}
// 1.定义一个HereNode 每个HearNode 对象就是一个节点// 定义StringLeLinkedList来管理人物
static class SingLeLinkedListTest{
// 先初始化一个头结点,一般头节点不要乱动private HeroNode head=new HeroNode(0,"","");
// 添加节点到单向链表public void add(HeroNode heroNode){// 思路:当不考虑这个编号的顺序时,找到当前链表的最后节点 2,将最后这个节点的next 指向新的节点// 因为head 头节点不能动,一次需要一个辅助指针tempHeroNode temp=head;
// 遍历链表:找到最后while (true){// 找到链表的最后if (temp.next==null){//满足这个条件 已经找到了break;}// 如果没有找到最后temp=temp.next;//就将temp后移}//当退出这个循环的时,temp指向了链表的最后temp.next=heroNode;//将节点指向新的节点,赋值}
//开始书写第二轮需求public void addByOrder(HeroNode heroNode){
// 因为头节点不能动 因此我们仍然通过一个辅助指针来帮助找到添加的位置// 因为单链表 因为我们找到temp是位于添加位置的前一个节点,否则就插入不了数据HeroNode temp=head;boolean flag=false;// flag标志添加的编号是否存在 默认为FALSEwhile (true){if (temp.next==null) //这个条件一旦满足,就说明temp已经在链表的最后{break;//这个时候不管有没有找到,都要退出循环}if (temp.next.no> head.no){//位置找到 就在temp的后面插入break;}else if(temp.next.no==heroNode.no){//这个条件满足就说明了heroNode的编号已经存在flag=true;//说明编号存在break;}temp=temp.next;//后移,变量当前链表}
// 现在要判断flag的值if (flag){//不能添加,说明编号存在System.out.println("准备插入的英雄的已经存在"+heroNode.no);}else {// 插入到链表中,temp的后面heroNode.next=temp.next;temp.next=heroNode;}}// 修改元素节点的信息 根据no编号来修改 即no编号不能修改public void update(HeroNode newHeroNode){
// 根据newHeroNode的no值来修改// 1.首先要判断链表是否为空if (head.next==null){System.out.println("链表为空");return;}
// 找到需要修改的这个节点
// 1.首先定义一个辅助变量HeroNode temp =head.next;boolean flag=false;//表示是否找到该节点while (true){if (temp==null){break;//说明链表已经遍历结束了}if (temp.no== newHeroNode.no){// 找到flag=true;break;}temp=temp.next;}
// 根据flag 判断是否找到要修改的节点if (flag){temp.name=newHeroNode.name;temp.nickname=newHeroNode.nickname;}else {// 否则就是没有找到System.out.println("没有找到,不能修改"+newHeroNode.no);}}// 显示链表:通过遍历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 nickname;public HeroNode next;//指向下一个节点
// 书写一个构造器public HeroNode(int no,String name,String nickname){this.no=no;this.name=name;this.nickname=nickname;}
// 为了显示方法,我们暂时重写toString
public String toString(){return "HeroNode [no="+no+",name="+name+",nickname="+nickname+"]";}
//需要按照编号的顺序添加:1.首先找到新添加的节点的位置,是通过辅助指针 2。让新的节点.next=temp.next
// 3.让temp.next=新的节点}}删除节点代码示例:思路:从单链表中删除一个节点的思路:1.我们首先找到需要删除的这个节点的前一个节点temp2.使temp.next=temp.next.next3.被删除的节点,将不会有其他引用指向,会被垃圾回收package 数据结构与算法.LinkedList;public class SingLeLinkedList {public static void main(String[] args) {
//开始测试以下代码;// 1.先创建节点HeroNode hero1 = new HeroNode(1, "吴邪", "小三爷");HeroNode hero2 = new HeroNode(2, "解雨臣", "小花");HeroNode hero3 = new HeroNode(3, "张起灵", "小哥");HeroNode hero4 = new HeroNode(4, "黑眼睛", "黑瞎子");// 2.创建链表SingLeLinkedListTest singLeLinkedListTest = new SingLeLinkedListTest();
// 3. 加入人物
// singLeLinkedListTest.add(hero1);
// singLeLinkedListTest.add(hero2);
// singLeLinkedListTest.add(hero3);
// singLeLinkedListTest.add(hero4);// 现在按照编号加入:singLeLinkedListTest.addByOrder(hero1);singLeLinkedListTest.addByOrder(hero4);singLeLinkedListTest.addByOrder(hero3);singLeLinkedListTest.addByOrder(hero2);// 修改前:System.out.println("修改前");singLeLinkedListTest.list();//测试修改节点的代码HeroNode newHeroNode=new HeroNode(2,"花","大宝贝");singLeLinkedListTest.update(newHeroNode);// 调用一把:System.out.println("修改后:");singLeLinkedListTest.list();// 删除节点:singLeLinkedListTest.del(1);System.out.println("删除后:");singLeLinkedListTest.list();}
// 1.定义一个HereNode 每个HearNode 对象就是一个节点// 定义StringLeLinkedList来管理人物
static class SingLeLinkedListTest{
// 先初始化一个头结点,一般头节点不要乱动private HeroNode head=new HeroNode(0,"","");
// 添加节点到单向链表public void add(HeroNode heroNode){// 思路:当不考虑这个编号的顺序时,找到当前链表的最后节点 2,将最后这个节点的next 指向新的节点// 因为head 头节点不能动,一次需要一个辅助指针tempHeroNode temp=head;
// 遍历链表:找到最后while (true){// 找到链表的最后if (temp.next==null){//满足这个条件 已经找到了break;}// 如果没有找到最后temp=temp.next;//就将temp后移}//当退出这个循环的时,temp指向了链表的最后temp.next=heroNode;//将节点指向新的节点,赋值}
//开始书写第二轮需求public void addByOrder(HeroNode heroNode){
// 因为头节点不能动 因此我们仍然通过一个辅助指针来帮助找到添加的位置// 因为单链表 因为我们找到temp是位于添加位置的前一个节点,否则就插入不了数据HeroNode temp=head;boolean flag=false;// flag标志添加的编号是否存在 默认为FALSEwhile (true){if (temp.next==null) //这个条件一旦满足,就说明temp已经在链表的最后{break;//这个时候不管有没有找到,都要退出循环}if (temp.next.no> head.no){//位置找到 就在temp的后面插入break;}else if(temp.next.no==heroNode.no){//这个条件满足就说明了heroNode的编号已经存在flag=true;//说明编号存在break;}temp=temp.next;//后移,变量当前链表}
// 现在要判断flag的值if (flag){//不能添加,说明编号存在System.out.println("准备插入的英雄的已经存在"+heroNode.no);}else {// 插入到链表中,temp的后面heroNode.next=temp.next;temp.next=heroNode;}}// 修改元素节点的信息 根据no编号来修改 即no编号不能修改public void update(HeroNode newHeroNode){
// 根据newHeroNode的no值来修改// 1.首先要判断链表是否为空if (head.next==null){System.out.println("链表为空");return;}
// 找到需要修改的这个节点
// 1.首先定义一个辅助变量HeroNode temp =head.next;boolean flag=false;//表示是否找到该节点while (true){if (temp==null){break;//说明链表已经遍历结束了}if (temp.no== newHeroNode.no){// 找到flag=true;break;}temp=temp.next;}
// 根据flag 判断是否找到要修改的节点if (flag){temp.name=newHeroNode.name;temp.nickname=newHeroNode.nickname;}else {// 否则就是没有找到System.out.println("没有找到,不能修改"+newHeroNode.no);}}//删除节点:1.head不能动 一次我们需要一个temp辅助节点找到待删除节点的前一个节点
//2.说明我们此时在比较时,是temp.next.no和需要删除的节点的no比较public void del(int no){// 拿到辅助节点:HeroNode temp=head;boolean flag=false;//表示是否找到待删除节点的前一个节点while (true){if (temp.next==null){//,满足这个条件,说明以及到链表的最后,不能在下去,只能退出break;}if (temp.next.no==no){
// 满足这个条件,就说明找到了待删除节点的前一个节点tempflag=true;break;}temp=temp.next;//temp后移 遍历}
// 判断flagif (flag){
// 满足这个条件就说明已经找到了,可以删除temp.next=temp.next.next;}else {// 没要找到,无法删除System.out.println("要删除的节点不存在");}}// 显示链表:通过遍历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 nickname;public HeroNode next;//指向下一个节点
// 书写一个构造器public HeroNode(int no,String name,String nickname){this.no=no;this.name=name;this.nickname=nickname;}
// 为了显示方法,我们暂时重写toString
public String toString(){return "HeroNode [no="+no+",name="+name+",nickname="+nickname+"]";}
//需要按照编号的顺序添加:1.首先找到新添加的节点的位置,是通过辅助指针 2。让新的节点.next=temp.next
// 3.让temp.next=新的节点}}小结:链表是以节点的方式来存储,是链式存储
2.每个节点包含data域 next域 指向下一个节点
3.链表的各个节点不一定是连续存储
4.链表分带头的链表和没有头节点的链表,根据实际的需求来确定单链表:
[head]---->[]---->[]---->
复习习题:
1.求单链表中有效节点的个数:package 数据结构与算法.LinkedList;public class SingLeLinkedList {public static void main(String[] args) {
//开始测试以下代码;// 1.先创建节点HeroNode hero1 = new HeroNode(1, "吴邪", "小三爷");HeroNode hero2 = new HeroNode(2, "解雨臣", "小花");HeroNode hero3 = new HeroNode(3, "张起灵", "小哥");HeroNode hero4 = new HeroNode(4, "黑眼睛", "黑瞎子");// 2.创建链表SingLeLinkedListTest singLeLinkedListTest = new SingLeLinkedListTest();
// 3. 加入人物
// singLeLinkedListTest.add(hero1);
// singLeLinkedListTest.add(hero2);
// singLeLinkedListTest.add(hero3);
// singLeLinkedListTest.add(hero4);// 现在按照编号加入:singLeLinkedListTest.addByOrder(hero1);singLeLinkedListTest.addByOrder(hero4);singLeLinkedListTest.addByOrder(hero3);singLeLinkedListTest.addByOrder(hero2);// 修改前:System.out.println("修改前");singLeLinkedListTest.list();//测试修改节点的代码HeroNode newHeroNode=new HeroNode(2,"花","大宝贝");singLeLinkedListTest.update(newHeroNode);// 调用一把:System.out.println("修改后:");singLeLinkedListTest.list();// 删除节点:singLeLinkedListTest.del(1);System.out.println("删除后:");singLeLinkedListTest.list();// 有效节点是:System.out.println("有效节点:");singLeLinkedListTest.getLength(singLeLinkedListTest.head);System.out.println(singLeLinkedListTest.getLength(singLeLinkedListTest.head)
);}
// 1.定义一个HereNode 每个HearNode 对象就是一个节点// 定义StringLeLinkedList来管理人物
static class SingLeLinkedListTest{// 方法:获取到单链表的节点的个数(如果是带头节点的链表,需求不统计头结点)public int getLength(HeroNode head){// head就链表的头结点 返回的就是有效节点的个数if (head.next==null){//满足这个条件就说明是空链表return 0;}int length=0;
// 定义一个辅助变量HeroNode cur=head.next;while (cur!=null){length++;cur=cur.next;}return length;}// 先初始化一个头结点,一般头节点不要乱动private HeroNode head=new HeroNode(0,"","");
// 添加节点到单向链表public void add(HeroNode heroNode){// 思路:当不考虑这个编号的顺序时,找到当前链表的最后节点 2,将最后这个节点的next 指向新的节点// 因为head 头节点不能动,一次需要一个辅助指针tempHeroNode temp=head;
// 遍历链表:找到最后while (true){// 找到链表的最后if (temp.next==null){//满足这个条件 已经找到了break;}// 如果没有找到最后temp=temp.next;//就将temp后移}//当退出这个循环的时,temp指向了链表的最后temp.next=heroNode;//将节点指向新的节点,赋值}
//开始书写第二轮需求public void addByOrder(HeroNode heroNode){
// 因为头节点不能动 因此我们仍然通过一个辅助指针来帮助找到添加的位置// 因为单链表 因为我们找到temp是位于添加位置的前一个节点,否则就插入不了数据HeroNode temp=head;boolean flag=false;// flag标志添加的编号是否存在 默认为FALSEwhile (true){if (temp.next==null) //这个条件一旦满足,就说明temp已经在链表的最后{break;//这个时候不管有没有找到,都要退出循环}if (temp.next.no> head.no){//位置找到 就在temp的后面插入break;}else if(temp.next.no==heroNode.no){//这个条件满足就说明了heroNode的编号已经存在flag=true;//说明编号存在break;}temp=temp.next;//后移,变量当前链表}
// 现在要判断flag的值if (flag){//不能添加,说明编号存在System.out.println("准备插入的英雄的已经存在"+heroNode.no);}else {// 插入到链表中,temp的后面heroNode.next=temp.next;temp.next=heroNode;}}// 修改元素节点的信息 根据no编号来修改 即no编号不能修改public void update(HeroNode newHeroNode){
// 根据newHeroNode的no值来修改// 1.首先要判断链表是否为空if (head.next==null){System.out.println("链表为空");return;}
// 找到需要修改的这个节点
// 1.首先定义一个辅助变量HeroNode temp =head.next;boolean flag=false;//表示是否找到该节点while (true){if (temp==null){break;//说明链表已经遍历结束了}if (temp.no== newHeroNode.no){// 找到flag=true;break;}temp=temp.next;}
// 根据flag 判断是否找到要修改的节点if (flag){temp.name=newHeroNode.name;temp.nickname=newHeroNode.nickname;}else {// 否则就是没有找到System.out.println("没有找到,不能修改"+newHeroNode.no);}}//删除节点:1.head不能动 一次我们需要一个temp辅助节点找到待删除节点的前一个节点
//2.说明我们此时在比较时,是temp.next.no和需要删除的节点的no比较public void del(int no){// 拿到辅助节点:HeroNode temp=head;boolean flag=false;//表示是否找到待删除节点的前一个节点while (true){if (temp.next==null){//,满足这个条件,说明以及到链表的最后,不能在下去,只能退出break;}if (temp.next.no==no){
// 满足这个条件,就说明找到了待删除节点的前一个节点tempflag=true;break;}temp=temp.next;//temp后移 遍历}
// 判断flagif (flag){
// 满足这个条件就说明已经找到了,可以删除temp.next=temp.next.next;}else {// 没要找到,无法删除System.out.println("要删除的节点不存在");}}// 显示链表:通过遍历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 nickname;public HeroNode next;//指向下一个节点
// 书写一个构造器public HeroNode(int no,String name,String nickname){this.no=no;this.name=name;this.nickname=nickname;}
// 为了显示方法,我们暂时重写toString
public String toString(){return "HeroNode [no="+no+",name="+name+",nickname="+nickname+"]";}
//需要按照编号的顺序添加:1.首先找到新添加的节点的位置,是通过辅助指针 2。让新的节点.next=temp.next
// 3.让temp.next=新的节点}}2.查找单链表中的倒数第k个节点:
1、书写一个方法 介绍head节点 同时接受一个index
2.index表示是倒数第index节点
3.先把链表从头到尾遍历 得到链表的总长度
4.得到size 从链表的第一个开始遍历 遍历(size-index)个 就可以得到:package 数据结构与算法.LinkedList;public class SingLeLinkedList {public static void main(String[] args) {
//开始测试以下代码;// 1.先创建节点HeroNode hero1 = new HeroNode(1, "吴邪", "小三爷");HeroNode hero2 = new HeroNode(2, "解雨臣", "小花");HeroNode hero3 = new HeroNode(3, "张起灵", "小哥");HeroNode hero4 = new HeroNode(4, "黑眼睛", "黑瞎子");// 2.创建链表SingLeLinkedListTest singLeLinkedListTest = new SingLeLinkedListTest();
// 3. 加入人物
// singLeLinkedListTest.add(hero1);
// singLeLinkedListTest.add(hero2);
// singLeLinkedListTest.add(hero3);
// singLeLinkedListTest.add(hero4);// 现在按照编号加入:singLeLinkedListTest.addByOrder(hero1);singLeLinkedListTest.addByOrder(hero4);singLeLinkedListTest.addByOrder(hero3);singLeLinkedListTest.addByOrder(hero2);// 修改前:System.out.println("修改前");singLeLinkedListTest.list();//测试修改节点的代码HeroNode newHeroNode=new HeroNode(2,"花","大宝贝");singLeLinkedListTest.update(newHeroNode);// 调用一把:System.out.println("修改后:");singLeLinkedListTest.list();// 删除节点:singLeLinkedListTest.del(1);System.out.println("删除后:");singLeLinkedListTest.list();// 有效节点是:System.out.println("有效节点:");singLeLinkedListTest.getLength(singLeLinkedListTest.head);System.out.println(singLeLinkedListTest.getLength(singLeLinkedListTest.head)
);// 测试是否得到倒数第k个元素HeroNode res= singLeLinkedListTest.findLastIndexNode(singLeLinkedListTest.head,1);System.out.println("倒数第1个"+res);}
// 1.定义一个HereNode 每个HearNode 对象就是一个节点// 定义StringLeLinkedList来管理人物
static class SingLeLinkedListTest{public HeroNode findLastIndexNode(HeroNode head,int index){// 判断链表为空,返回nullif (head.next==null){return null;}/*第一次遍历得到链表的长度* */int size=getLength(head);
// 第二次遍历 size-index 位置 就是倒数的第k个节点
// 先检验if (index<=0||index>size){return null;}// 定义一个辅助变量:HeroNode cur=head.next;
// 使用循环定位到倒数的indexfor (int i=0;i<size-index;i++){cur=cur.next;}return cur;
}// 方法:获取到单链表的节点的个数(如果是带头节点的链表,需求不统计头结点)public int getLength(HeroNode head){// head就链表的头结点 返回的就是有效节点的个数if (head.next==null){//满足这个条件就说明是空链表return 0;}int length=0;
// 定义一个辅助变量HeroNode cur=head.next;while (cur!=null){length++;cur=cur.next;}return length;}// 先初始化一个头结点,一般头节点不要乱动private HeroNode head=new HeroNode(0,"","");
// 添加节点到单向链表public void add(HeroNode heroNode){// 思路:当不考虑这个编号的顺序时,找到当前链表的最后节点 2,将最后这个节点的next 指向新的节点// 因为head 头节点不能动,一次需要一个辅助指针tempHeroNode temp=head;
// 遍历链表:找到最后while (true){// 找到链表的最后if (temp.next==null){//满足这个条件 已经找到了break;}// 如果没有找到最后temp=temp.next;//就将temp后移}//当退出这个循环的时,temp指向了链表的最后temp.next=heroNode;//将节点指向新的节点,赋值}
//开始书写第二轮需求public void addByOrder(HeroNode heroNode){
// 因为头节点不能动 因此我们仍然通过一个辅助指针来帮助找到添加的位置// 因为单链表 因为我们找到temp是位于添加位置的前一个节点,否则就插入不了数据HeroNode temp=head;boolean flag=false;// flag标志添加的编号是否存在 默认为FALSEwhile (true){if (temp.next==null) //这个条件一旦满足,就说明temp已经在链表的最后{break;//这个时候不管有没有找到,都要退出循环}if (temp.next.no> head.no){//位置找到 就在temp的后面插入break;}else if(temp.next.no==heroNode.no){//这个条件满足就说明了heroNode的编号已经存在flag=true;//说明编号存在break;}temp=temp.next;//后移,变量当前链表}
// 现在要判断flag的值if (flag){//不能添加,说明编号存在System.out.println("准备插入的英雄的已经存在"+heroNode.no);}else {// 插入到链表中,temp的后面heroNode.next=temp.next;temp.next=heroNode;}}// 修改元素节点的信息 根据no编号来修改 即no编号不能修改public void update(HeroNode newHeroNode){
// 根据newHeroNode的no值来修改// 1.首先要判断链表是否为空if (head.next==null){System.out.println("链表为空");return;}
// 找到需要修改的这个节点
// 1.首先定义一个辅助变量HeroNode temp =head.next;boolean flag=false;//表示是否找到该节点while (true){if (temp==null){break;//说明链表已经遍历结束了}if (temp.no== newHeroNode.no){// 找到flag=true;break;}temp=temp.next;}
// 根据flag 判断是否找到要修改的节点if (flag){temp.name=newHeroNode.name;temp.nickname=newHeroNode.nickname;}else {// 否则就是没有找到System.out.println("没有找到,不能修改"+newHeroNode.no);}}//删除节点:1.head不能动 一次我们需要一个temp辅助节点找到待删除节点的前一个节点
//2.说明我们此时在比较时,是temp.next.no和需要删除的节点的no比较public void del(int no){// 拿到辅助节点:HeroNode temp=head;boolean flag=false;//表示是否找到待删除节点的前一个节点while (true){if (temp.next==null){//,满足这个条件,说明以及到链表的最后,不能在下去,只能退出break;}if (temp.next.no==no){
// 满足这个条件,就说明找到了待删除节点的前一个节点tempflag=true;break;}temp=temp.next;//temp后移 遍历}
// 判断flagif (flag){
// 满足这个条件就说明已经找到了,可以删除temp.next=temp.next.next;}else {// 没要找到,无法删除System.out.println("要删除的节点不存在");}}// 显示链表:通过遍历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 nickname;public HeroNode next;//指向下一个节点
// 书写一个构造器public HeroNode(int no,String name,String nickname){this.no=no;this.name=name;this.nickname=nickname;}
// 为了显示方法,我们暂时重写toString
public String toString(){return "HeroNode [no="+no+",name="+name+",nickname="+nickname+"]";}
//需要按照编号的顺序添加:1.首先找到新添加的节点的位置,是通过辅助指针 2。让新的节点.next=temp.next
// 3.让temp.next=新的节点}}
.
数据结构(Java版2022-10-29)相关推荐
- 【不忘初心】Win10_20H2_2009_19042.572_X64_六合一_[纯净精简版](2020.10.29)
母版来自MSDN WIN10_20H2.19042.508,集成补到19041.572,20H2相比1909 2004版本要稳定很多,精简起来也比较顺手,相对来说体积比之前的要小一些,精简方法基本上 ...
- 国产服务器软件 LinWinHttp 重大更新 V1.3 Community Build 2022.10.29 发布,这次的更新有什么内容?
国产服务器 LinWIn Http LinWIn Http 由萤火科技团队开发,专门用于国产操作系统以及 Linux 操作系统的一种开源 HTTP 服务器软件.具有快速部署.快速反应.便捷安全.上手简 ...
- 信奥中的数学:对数(2022.10.29)
[仙童小学]你真的认识对数吗 [仙童小学]你真的认识对数吗_哔哩哔哩_bilibili 小学生对数学的理解 5年级 小学生对数学的理解 5年级_哔哩哔哩_bilibili [秒懂数学]对数运算难如狗, ...
- 数据结构(java版)SortedSeqList(排序顺序表)
SortedSeqList(排序顺序表) 代码部分: public class SortedSeqList<T extends Comparable<? super T>> e ...
- 数据结构java版txt,图解数据结构:使用Java
图解数据结构:使用Java 下载 mobi epub pdf ☆☆☆☆☆ 胡昭民 著 下载链接在页面底部 发表于2021-03-10 图书介绍 出版社: 清华大学出版社 ISBN:9787302402 ...
- 数据结构(Java版 2022-10-30)
第一章:算法介绍 数据结构与算法面试题" 一.字符串匹配问题:有一个字符串str1="计算机科学与技术学院欢迎您!" 和另一个字符串 str2="计算机科学与技 ...
- 数据结构Java版之基数排序(四)
基数排序: 基数排序分为两种:第一种是LSD ,从最低位开始排序, 第二种是 MSD 从最高位开始排.这里介绍第一种LSD排序算法. 首先,我们先了解什么是基数.基数是根据具体的排序情况而定的,比如我 ...
- 数据结构Java版实验五_实验五数据结构综合应用 20162310
分析系统架构 Sprite精灵类 ISprite精灵类是所有类的父类 CombatAircraft战斗机类 首先确保战斗机完全位于Canvas范围内,每隔7帧发射单发黄色子弹. protected v ...
- java冒泡法优化_数据结构java版之冒泡排序及优化
冒泡排序的时间用大O表示法是O(N^2). 传统的冒泡排序: /** * @param total 要排序的数组长度 */ public void sort(int total){ int num[] ...
最新文章
- 基础搭建Hadoop大数据处理-编程
- html怎样做登录页面,使用HTML 5和CSS3制作登录页面完整步骤
- 为什么LED灯会越用越暗?
- 创建存储过程时出现的This function has none of DETERMINISTIC, NO SQL解决办法
- 疫情之下网络安全如何保障?Akamai防护方案前来“保驾护航”
- Sqlit--学习教程(基本操作1)
- 计算方法上机实验c语言,计算方法与实习实验报告c语言.pdf
- 小网站服务器空间,小型网站空间服务器
- ppt倒计时3分钟_老板发来200页PPT文件,让我翻译成英文,3分钟教你搞定
- sql交叉表查询_初学前端需要注意什么 SQL连接相关内容有哪些
- html 中的特殊字符转义,html拼接字符串中特殊字符(‘ “ 等的转义问题)
- MySQL查询时当offset较大时查询效率低
- app用户注册、登录原理、注册页面
- python 为女神编朵玫瑰花的代码_小伙利用Python绘制999种玫瑰花表白女神,会编程男孩子真好...
- iphone 扩容测试软件,给iPhone扩容!闪迪手机U盘测评
- 数论的一个基础计算器,集成了同余式,逐次平方法,勒让德计算,模M的K次密等内容
- 得中原者得天下!2018中国软件生态大会郑州站火爆进行
- DPU芯片企业中科驭数加入龙蜥社区,构建异构算力生态
- 哈工大 编译原理 复习笔记
- 查看TensorFlow checkpoint文件中的变量名和对应值
热门文章
- 如何防止文件被备份到iCloud 和iTunes?
- 免费|DuckChat聊天系统开源可二开源码下载快速搭建聊天系统
- DWORD转LPCSTR
- lol无限火力服务器崩溃,LOL:无限火力把服务器挤崩溃了?玩家集体开始吐槽,玩的是心态...
- 服务器出现卡顿几个处理技巧
- Google输入法 or Outlook2003 Bug
- python五子棋单机版源代码_python 五子棋 游戏源码(python 2.7入门级)
- FORTRAN+计算物理学学习日记(5)
- 计算机运行速度慢怎样解决方法,电脑运行速度慢的解决方法:瞬间加快电脑运行速度妙招...
- 货物与产品的区别_辨析货物与商品的不同,定义什么是价值,什么是劳动