数据结构

1.  几个景点算法:

     1. 修路问题:最小生成树(加权值)+ 普利姆

     2.   最短路径:图+弗洛伊德算法

     3.   汉诺塔: 分支的算法

     4.  八皇后:回朔法、

     5. 丢手帕  : 约瑟夫问题

2. 线性结构 与非线性结构

       1.线性结构 :数据元素之间存在一对一的线性关系

             顺序存储结构 ,  链式存储结构

             (数组,队列,链表和栈)

 

       2 . 非线性结构:(二维数组,多维数组,广义表,树结构,图结构)

 

3. 稀疏数组

                

             因为该数组中记录了很多值是默认值0,记录了很多没有意义的数据  -》 稀疏数组

                 

 

                

 

代码实现原始二维数组与稀疏数组之间的转化

                         

 

 

import org.junit.Test;

public class XiShuJuZhen {
    
    //创建一个原始的二维数组 11*11
    @Test
    public void testArray() {
         int[][] charArray =  new int[11][11];
         //0表示没有棋子,1 表示黑子  2表示蓝子
            charArray[1][2] = 1;
            charArray[2][3] = 2;
            charArray[4][5] = 2;
            System.out.println("输出原始的二维数组");
            for (int[] is : charArray) {
                for (int is2 : is) {
                    System.out.print("\t"+is2);
                }
                System.out.println();
                
            }
            /*
             * 将二维数组转为稀疏数组的思路
             * (1).先遍历二维数组,得到非0整数的个数
             */
            int sum =  0;
            for(int i=0;i<11;i++) {
                for(int j=0;j<11;j++) {
                    if(charArray[i][j]!=0) {
                        sum++;
                    }
                }
            }
            System.out.println("sum = "+sum);
            
            //2 .创建对应的稀疏数组
            int sparseArr[][] = new int[sum+1][3] ;
            //给稀疏数组赋值
            sparseArr[0][0] = 11; //有11行
            sparseArr[0][1] = 11;  //有11列
            sparseArr[0][2] = sum; //有多少个值
            
            int count= 0; //count 用于记录是第几个非0数据
            for(int i=0;i<11;i++) {
                for(int j=0;j<11;j++) {
                    if(charArray[i][j]!=0) {
                        count++;
                        sparseArr[count][0] = i;  //第0 列,第count行
                        sparseArr[count][1] = j;  //第1列,第count行
                        sparseArr[count][2]  =charArray[i][j];//第2列 存入的是二维数组不为0的值
                        
                    }
                }
            }
            System.out.println("输出稀疏数组的形式");
            for (int[] is : sparseArr) {
                for (int is2 : is) {
                    System.out.print("\t"+is2);
                }
                System.out.println();
            }
            //恢复成二维数组
            //先读取稀疏数组的第一行将新创建的数组进行赋值确定数组大小
            int[][] charArray1 = new int[sparseArr[0][0]][sparseArr[0][0]];
            
            //再读取稀疏数组的后几行,从第二行开始,并赋给原始的二维数组
            for(int i=1;i<sparseArr.length;i++) {
                //为原始数组进行赋值
                charArray1[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
            }
            System.out.println("恢复后的原始二维数组");
            for (int[] is : charArray1) {
                for (int is2 : is) {
                    System.out.print("\t"+is2);
                }
                System.out.println();
            }
    }    
}
代码实现

 使用IO流将数据保存起来 其实我没有明白是什么意思,然后我就写了一个IO流将数据以文本的方式保存起来了

                         

 

public static void main(String[] args) throws IOException {
        //创建一个字节流输出流
        FileOutputStream fileOutputStream = new FileOutputStream("D:/lucenezhulina/a.txt");
        
        //将字节流转化为字符流
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
        
        //使用缓冲区进行缓冲
         BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
         
    //     bufferedWriter.write("朱李纳");
         
         //0表示没有棋子,1 表示黑子  2表示蓝子
            String str1 = "int[][] charArray =  new int[11][11];";
            String str2 = "charArray[1][2] = 1;";
            String str3 = "charArray[2][3] = 2;";
            String str4 = "charArray[4][5] = 2;";
            
        bufferedWriter.write(str1);
        
        bufferedWriter.write(str2);
        bufferedWriter.write(str3);
        bufferedWriter.write(str4);
        bufferedWriter.close();
        
         //创建 一个字节输入流
         FileInputStream fileInputStream = new  FileInputStream("D:/lucenezhulina/a.txt");
         
         //将字节流转化为字符流
         InputStreamReader inputStreamReader = new  InputStreamReader(fileInputStream);
        
         //使用缓冲流
         BufferedReader bufferedReader = new  BufferedReader(inputStreamReader);
         String readLine = bufferedReader.readLine();
         System.out.println(readLine);
         //读取数据
        /* while(true) {
             String readLine = bufferedReader.readLine();
             if(readLine==null) {
                 break;
             }
             System.out.println(readLine);
         }*/
         
         bufferedReader.close();
    }
代码

改进后的代码

                            

import java.awt.event.FocusAdapter;
import java.util.Scanner;

import javax.management.RuntimeErrorException;

import org.junit.Test;

public class CircleQueue {
     
    private int  maxSize;//表示数组的最大容量
    private int front;//front指向队列的第一个元素
    private int rear;//队列尾
    private int arr[];//该数组用于存放数据,模拟队列
    
    
    //定义一个构造器给与数组初始大小
    public CircleQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
    }
    
    //判断队列是否满
    public boolean isFull() {
        
        return (rear+1) % maxSize == front; //rear+1 最大和maxSize相同
    }
    
    //添加数据到队列
    public void addQueue(int n) {
        //判断队列是否满
        if(isFull()) {
            System.out.println("队列已满,不能加入");
            return;
        }
        arr[rear] = n;
        rear = (rear+1)%maxSize; //rear不可能比maxSize大因此余数就是rear+1,也就是rear向后移动一个值
    }
    //判断队列是否为空
    public boolean isEmpty() {
        
        return rear==front;
    }
    //获取队列的数据出队列
    public int getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,不能取数据");
        }
        int value = arr[front];
         front = (front+1)%maxSize; //front最大是与rear相同比maxSize小1
      return value;
    }
    //显示队列的所有数据
    public void showQueue() {
        //遍历
        if(isEmpty()) {
            System.out.println("队列是空的,没有数据");
            return;
        }
        for(int i=front;i<front+size();i++) {
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }
    //求出当前列有效数据个数
    public int size() {
        return (rear+maxSize -front)%maxSize;
    }
    
    //显示头信息
    public int  headQueue() {
        //判断
    if(isEmpty()) {
        System.out.println("队列为空,没有投数据");
        throw new RuntimeException("队列空的没有数据");
    }
        return arr[front];
    
    }
}

public class TestCircleQueue {
    @Test
    public  void test01() {
        //创建一个队列对象
        CircleQueue arrayQueue = new CircleQueue(4);
        //接收用户输入
        char key = ' ';
        Scanner sc = 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 = sc.next().charAt(0);//接收一个字符
            switch (key) {
            case 's':
                arrayQueue.showQueue();
                break;
            case 'e': //退出程序
                sc.close();
                loop = false;
                break;
            case 'h': //查看队列头的数据
                try {
                   int res = arrayQueue.headQueue();
                   System.out.println("队列头的数据是: "+res);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
                
                break;
            case 'a':
                System.out.println("输入一个数");
                int value = sc.nextInt();
                arrayQueue.addQueue(value);
                break;
            case 'g':  //取出数据
                try {
                    int res = arrayQueue.getQueue();
                    System.out.println("取出的数据是:"+res);
                }catch (Exception e) {
                    System.out.println(e.getMessage());
                }
                break;

            default:
                break;
            }
        }
        System.out.println("程序退出");
    }
}
具体代码实现

 使用循环链表,这种方式不能进行排序,如果插入的顺序不一样,不能自动的进行排序

                           

 

 

class SingleLinkedListDemo {
    public static void main(String[] args) {
        //进行测试
        //先创建节点
        HeroNode heroNode1 = new HeroNode(1, "宋江" , "及时雨");
        HeroNode heroNode2 = new HeroNode(2, "卢俊义" , "玉麒麟");
        HeroNode heroNode3 = new HeroNode(3, "吴用" , "智多星");
        HeroNode heroNode4 = new HeroNode(4, "林冲" , "豹子头");
        
        //创建链表
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        //加入
    /*    singleLinkedList.add(heroNode1);
        singleLinkedList.add(heroNode2);
        singleLinkedList.add(heroNode3);
        singleLinkedList.add(heroNode4);*/
        
        //加入按照编号的顺序
        singleLinkedList.addByOrder(heroNode1);
        singleLinkedList.addByOrder(heroNode4);
        singleLinkedList.addByOrder(heroNode2);
        singleLinkedList.addByOrder(heroNode3);
        //遍历
        singleLinkedList.list();
        
    }
}
//定义SingleLinkedList 管理我们的英雄
class SingleLinkedList{
    //先初始化一个头节点,头节点不要动,不存放具体的数据
    private HeroNode head = new HeroNode(0,"","");
    
    //添加节点到单向链表
    /**
      * 思路:当不考虑编号顺序时
     * 1.找到当前链表的最后节点
     * 2.将最后节点的这个next指向新的节点
     */
    public void add(HeroNode heroNode) { 
        //因为head节点不能动,因此我们需要一个辅助遍历temp
        HeroNode temp = head;
        //遍历链表,找到最后
        while(true) {
            //找到链表的最后
            if (temp.next==null) {
                break;
            }
            //如果没有找到就将temp后移
            temp=temp.next;
        }
        //当退出while循环时,temp就指向了链表的最后
        //将最后节点的这个next指向新的节点
        temp.next = heroNode;
    }
    //第二种方式在添加英雄时,根据排名将英雄添加到指定位置
    //如果有这个排名,择添加失败并给出提示
    public void addByOrder(HeroNode heroNode) {
        //应为头结点不能动,我们通过一个辅助变量帮助找到添加的位置
        HeroNode temp = head;
        boolean flag = false; //标识添加的这个编号是否存在默认是false
        while (true) {
            if(temp.next==null) {//说明temp节点已经在链表的最后
                break;
            }
            if (temp.next.no>heroNode.no) {//位置找到,就在temp的后面插入
                break;
                
            }else if(temp.next.no == heroNode.no) {//说明希望添加的heroNode的编号已然存在
                flag = true;//说明编号存在
                break;
            }
            temp = temp.next; //后移,遍历当前的链表
        }
        
        
        //判断flag的值
        if (flag) { //不能添加,说明编号存在
            System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",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);
             //将这个temp后移
             temp = temp.next;
        }
        
    }

    
}
//定义HeroNode ,每一个HeroNode对象就是一个节点
class HeroNode{
    public int no;
    public String name;
    public String nickname; //昵称
    public HeroNode next; //指向下一个节点
    
    //构造器  传入三个参数一个编号,姓名,昵称
    public HeroNode(int hNo,String hName,String hNickName) {
         this.no = hNo;
         this.name = hName;
         this.nickname = hNickName;
    }

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + ", next=" + next + "]";
    }
    
    
    
    
    
    
    
}
具体代码实现

 单链表的修改操作

                                

//修改节点的信息,根据no编号来修改,即no不能改
    public void update(HeroNode newHeroNode) {
        //判断是否为空
        if (head.next==null) {
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点,根据no编号
        //定义一个辅助变量
        HeroNode temp = head.next;
        boolean flag = false;  //表示是否找到该节点
        while(true) {
            if(temp==null) {
                break;//已经遍历完链表 在遍历的过程中temp一直在向后走到达最后一个next为null
            }if (temp.no == newHeroNode.no) {
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否找到要修改的节点
        if(flag==true) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        }else {
            //没有找到
            System.out.printf("没有找到编号为 %d 的节点,不能修改\n",newHeroNode.no);
        }
    }
    
修改后的代码
    //测试修改节点的代码
        HeroNode heroNode = new HeroNode(2, "小卢", "玉麒麟");
        singleLinkedList.update(heroNode);
        
        System.out.println("修改后");
        singleLinkedList.list();
测试代码

                                

 删除节点

                                     

 

 

//删除节点
    public void delete(int no) {
        HeroNode temp = head;
        boolean flag = false; //标志是否找到待删除节点的前一个节点
        while(true) {
            if (temp.next==null) { //已经遍历到了链表尾
                break;
            }
            if (temp.next.no ==no) {
                //找打了待删除节点的前一个节点
                flag = true;
                break;
            }
            //若没有找到temp后移继续遍历
            temp  = temp.next;
        }
        //判断flag 
        if (flag ==true) {
            //找到
            temp.next = temp.next.next;
        }else {
            System.out.printf("要删除的 %d 节点不存在\n",no);
        }
        
    }
    
删除节点代码
//删除一个节点
        singleLinkedList.delete(1);
        System.out.println("删除后链表结构");
        singleLinkedList.list();
删除节点代码测试

 几个经典面试题:

将单链表进行翻转

//将单链表进行翻转
    public static void reverseLinked(HeroNode heroNode) {
        //如果当前链表为空,或者只有一个节点就无需翻转 直接返回
        if (heroNode.next==null|| heroNode.next.next==null) {
            return ;
        }
        //定义一个辅助的指针,帮我们遍历原来的链表
        HeroNode current =heroNode.next;
        HeroNode next = null; //指向当前节点current的下一个节点 相当一next代替current向后移动
        HeroNode reverseHead = new HeroNode(0, "", "");
        //遍历原来的链表每遍历一个节点就将其取出,并放在新的链表reverseHead的最前端
        
        while(current !=null) {
            next = current.next; //先暂时保留current的下一个节点
            current.next =  reverseHead.next; //将当前节点的下一个节点置为空 目的是使
            //    当前current 称为新链表的第一个节点
            reverseHead.next = current; //当前节点称为了新链表的第一个节点
            current   = next; //现在current指向了原来链表的下一个节点,也就是让current后移
        }
        //最后将head.next指向reverseHead.next,实现单链表的反转
        heroNode.next = reverseHead.next;
    }
    
将单链表进行反转

测试:

    //测试单链表的反转
        System.out.println("反转后链表的情况");
        reverseLinked(singleLinkedList.getHead());        
        singleLinkedList.list();
测试

 

查找单链表中的倒数第k个节点 新浪面试题

//查找单链表中的倒数第k个节点 新浪面试题
    /*
     * 思路:编写一个方法,接收head节点同时接收一个index,index 表示倒数第index个节点
     * 先把链表从头到尾遍历,然后减去index就是我需要遍历的长度
     */
    
    public static HeroNode returnSignIndexNode(HeroNode heroNode,int index) {
        //如果链表尾空就返回null
        if (heroNode.next==null) {
            return null;
        }
        //第一次遍历获取链表的长度
        int size = getLength(heroNode);
        
        //第二次遍历 size-index位置就是我们倒数的第k的节点
        //先做一个index的校验
        if(index<=0||index>size) {
            return null;
        }
        //定义辅助变量,for循环定位到倒数的index
        HeroNode current = heroNode.next;
        for(int i=0;i<size-index;i++) {
            current = current.next;
        }
        return current;
    }
    
倒数第k个节点

测试:

//测试是否得到了倒数第k个节点
        HeroNode res = returnSignIndexNode(singleLinkedList.getHead(), 1);
        System.out.println("倒数第1 res = "+res);
测试

 

查找单链表有效节点的个数不统计头结点

//查找单链表有效节点的个数不统计头结点
    public static int getLength(HeroNode heroNode) {
        
        if(heroNode.next ==null) { //空链表
            
             return 0;
        }
        int count = 0;
        HeroNode current;
        current = heroNode.next;
        while(current!=null) {
            count++;
            current = current.next;
        }
        return count;
    }
View Code

测试:

//测试一下 求单链表中有效节点的个数
        System.out.println("有效的节点个数:"+getLength(singleLinkedList.getHead()));
测试

 从尾到头打印单链表

 /*
        * 从尾到头打印单链表
     *  思路:方式一:先将单链表进行翻转操作,然后再遍历即可,这样做的问题是会破坏原来的单链表的结构
     *  不建议。
     *  方式二:可以利用栈这个数据结构,将个个节点压入栈中,然后利用栈的先进后出的特点,就实现了
     *  逆序打印的效果。
     */
    public static void  reversePrint(HeroNode heroNode) {
        if(heroNode.next==null) {
            return; //空链表,不能打印
        }
        //创建一个栈,将各个节点压入栈中
        Stack<HeroNode> stack = new Stack<HeroNode>();
        
        HeroNode current = heroNode.next;
        //将链表的所有的节点压入栈中
        while(current!=null) {
            stack.push(current);
            current = current.next; //current后移
        }
        //将栈中的节点进行打印,pop 出栈
        while(stack.size()>0) {
            System.out.println(stack.pop());//先进后出    出栈
        }
    }
    
View Code

测试

  System.out.println("测试逆序打印单链表,没有改变原来链表的结构");
        reversePrint(singleLinkedList.getHead());
测试

 

 

 

 

 

 

 

 

 

 

 

 

 

 

               

 

posted @ 2019-09-24 10:32  纳兰容若♫  阅读(190)  评论(0编辑  收藏  举报