数据结构学习笔记【持续更新。。】
队列
队列是一个有序列表,可以用数组 或者链表 实现。(数组实现是顺序存储,链表实现是链式存储)。
遵循先入先出 的原则,及现存如的数据,要先取出。后存入的数据要后取出。
数组模拟队列
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 package day1队列;public class ArrayQueueDemo { public static void main (String[] args) { ArrayQueue queue = new ArrayQueue(3 ); queue.addQueue(1 ); queue.addQueue(2 ); queue.addQueue(3 ); System.out.println(queue.headQueue()); int i = queue.getQueue(); System.out.println(queue.headQueue()); } } class ArrayQueue { private int maxSize; private int front; private int rear; private int [] arr; public ArrayQueue (int maxSize) { this .maxSize = maxSize; this .arr = new int [maxSize]; front = -1 ; 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++; arr[rear] = n; } public int getQueue () { if (isEmpty()){ throw new RuntimeException("队列为空,不能取数据" ); } front++; return arr[front]; } public void showQueue () { if (isEmpty()){ System.out.println("队列为空,没有数据" ); return ; } for (int i = front + 1 ; i < arr.length; i++) { System.out.println(arr[i]); } } public int headQueue () { if (isEmpty()){ throw new RuntimeException("队列空的,没有数据" ); } return arr[front + 1 ]; } }
数据不能重复使用。
数组模拟环形队列
思路调整
front变量的含义做一个调整:front就是指向队列的第一个元素,front的初始值为0。
rear变量的含义做一个调整:rear指向队列的最后元素的后一个位置,rear的初始值为0。
当队列满时,条件是(rear + 1) % maxSize = front
队列为空时,条件是rear == front
队列中有效数据的个数(rear + maxSize - front) % maxSize
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 package day1队列;public class CircleArrayQueueDemo { public static void main (String[] args) { CircleArrayQueue queue = new CircleArrayQueue(4 ); queue.addQueue(1 ); queue.addQueue(2 ); queue.addQueue(3 ); int i = queue.getQueue(); int i2 = queue.getQueue(); queue.showQueue(); System.out.println(queue.size()); System.out.println(queue.headQueue()); } } class CircleArrayQueue { private int maxSize; private int front; private int rear; private int [] arr; public CircleArrayQueue (int maxSize) { this .maxSize = maxSize; this .arr = new int [maxSize]; front = 0 ; rear = 0 ; } 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 + 1 ) % maxSize; } public int getQueue () { if (isEmpty()){ throw new RuntimeException("队列为空,不能取数据" ); } int val = arr[front]; front = (front + 1 ) % maxSize; return val; } 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 headQueue () { if (isEmpty()){ throw new RuntimeException("队列空的,没有数据" ); } return arr[front]; } public int size () { return (rear + maxSize - front) % maxSize; } }
链表 .png)
.png)
链表以节点的方式来存储,是链式存储。
每个节点包含data域,next域(指向下一个节点)。
内内地址不一定连续 。
链表分为带头结点的链表 和不带头节点的链表 。
单链表的实际应用
使用带头结点的单链表 实现-水浒英雄排行榜管理
1)完成对英雄人物的增删改查
2)第一种方式在添加英雄时,根据排名将英雄插入到链表的尾部
3)第二种方式在添加英雄时,根据排名将英雄插入到指定位置
.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 package day2链表;public class SingleLinkedListDemo { public static void main (String[] args) { SingleLinkedList singleLinkedList = new SingleLinkedList(); singleLinkedList.add(new HeroNode(1 ,"松江" ,"及时雨" )); singleLinkedList.add(new HeroNode(2 ,"卢俊义" ,"玉麒麟" )); singleLinkedList.add(new HeroNode(3 ,"吴用" ,"智多星" )); singleLinkedList.add(new HeroNode(4 ,"林冲" ,"豹子头" )); singleLinkedList.list(); } } class SingleLinkedList { private HeroNode head = new HeroNode(0 ,"" ,"" ); public void add (HeroNode heroNode) { HeroNode temp = head; while (temp.next != null ){ temp = temp.next; } temp.next = heroNode; } public void list () { if (head.next == null ){ System.out.println("链表为空" ); return ; } HeroNode temp = head; while (temp.next != null ){ System.out.println(temp.next); temp = temp.next; } } } 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; } @Override public String toString () { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}' ; } }
第二种方式在添加英雄时,根据排名将英雄插入到指定位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public void addByOrder (HeroNode heroNode) { HeroNode temp = head; boolean flag = false ; while (true ){ if (temp.next == null ){ break ; } if (temp.next.no > heroNode.no){ break ; } else if (temp.next.no == heroNode.no){ flag = true ; break ; } temp = temp.next; } if (flag == true ){ System.out.printf("待插入的英雄的编号[%d]已经存在了,不能加入\n" ,heroNode.no); }else { heroNode.next = temp.next; temp.next = heroNode; } }
删除节点
思路:
首先找到要删除节点的前一个节点temp
temp.next = temp.next.next
被删除的节点,没有任何引用指向它,将会被GC回收
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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.next; } if (flag){ temp.next = temp.next.next; }else { System.out.printf("没有找到待删除的节点[%d]\n" ,no); } }
单链表常见面试题 1.单链表的反转
思路:
先定义一个节点reverseHead = new Node();
从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新链表reverseHead的最前端
原来的链表的head.next = reverseHead.next
双向链表
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 package day3双向列表;public class DoubleLinkedListDemo { public static void main (String[] args) { DoubleLindedList doubleLindedList = new DoubleLindedList(); doubleLindedList.add(new Node(1 ,"宋江" )); doubleLindedList.add(new Node(2 ,"卢俊义" )); doubleLindedList.add(new Node(3 ,"吴用" )); doubleLindedList.add(new Node(4 ,"林冲" )); doubleLindedList.list(); doubleLindedList.update(new Node(3 ,"公孙胜" )); doubleLindedList.list(); doubleLindedList.delete(2 ); doubleLindedList.list(); } } class DoubleLindedList { private Node head = new Node(0 ,"" ); public Node getHead () { return head; } public void list () { if (head.next == null ){ System.out.println("链表为空" ); return ; } Node temp = head.next; while (true ){ if (temp.next == null ){ break ; } System.out.println(temp); temp = temp.next; } } public void add (Node node) { Node temp = head; while (true ){ if (temp.next == null ){ break ; } temp = temp.next; } temp.next = node; node.pre = temp; } public void update (Node node) { if (head.next == null ){ System.out.println("链表为空" ); return ; } Node temp = head; boolean flag = false ; while (true ){ if (temp == null ){ break ; } if (temp.no == node.no){ flag = true ; break ; } temp = temp.next; } if (flag){ temp.name = node.name; }else { System.out.printf("没有找到编号为[%d]的节点" ,node.no); } } public void delete (int no) { if (head.next == null ){ System.out.println("链表为空,无法删除" ); return ; } Node temp = head.next; boolean flag = false ; while (true ){ if (temp == null ){ break ; } if (temp.no == no){ flag = true ; break ; } temp = temp.next; } if (flag){ temp.pre.next = temp.next; if (temp.next != null ){ temp.next.pre = temp.pre; } } } } class Node { public int no; public String name; public Node next; public Node pre; public Node (int no, String name) { this .no = no; this .name = name; } @Override public String toString () { return "Node{" + "no=" + no + ", name='" + name + '\'' + '}' ; } }
单向环形链表
约瑟夫问题
约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
假设n=5 ,即有5个人。
k=1 ,从第一个人开始报数
m=2 ,数两下
构建一个单向循环链表的的思路 (curNode的next指向新的元素,新的元素的next指向first,curNode后移)
先构建第一个节点,让first指向该节点,并形成环。
后面我们没创建一个新的节点,就把该节点,加入到已有的环形链表即可
遍历环形链表
先让一个辅助指针指向first节点
后通过一个while循环遍历该环形链表即可(current.next == first)
出圈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 package day4单向循环链表;public class Josepfu { public static void main (String[] args) { CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList(); circleSingleLinkedList.addNode(5 ); circleSingleLinkedList.out(1 ,2 ,5 ); } } class CircleSingleLinkedList { private Node first = null ; public void addNode (int nums) { if (nums < 1 ){ throw new RuntimeException("非法参数" ); } Node curNode = null ; for (int i = 1 ; i <= nums; i++) { Node node = new Node(i); if (i == 1 ){ first = node; first.setNext(first); curNode = first; }else { curNode.setNext(node); node.setNext(first); curNode = node; } } } public void list () { if (first == null ){ System.out.println("链表为空" ); return ; } Node curNode = first; while (true ){ System.out.printf("NODE节点的编号 %d \n" ,curNode.getNo()); if (curNode.getNext() == first){ break ; } curNode = curNode.getNext(); } } public void out (int startNode,int countNum,int nums) { if (first == null || startNode < 1 || startNode > nums){ throw new RuntimeException("非法参数" ); } Node helper = first; while (helper.getNext() != first) { helper = helper.getNext(); } for (int i = 0 ; i < startNode - 1 ; i++) { first = first.getNext(); helper = helper.getNext(); } while (true ){ if (helper == first){ break ; } for (int i = 0 ; i < countNum - 1 ; i++) { first = first.getNext(); helper = helper.getNext(); } System.out.printf("节点%d出圈 \n" ,first.getNo()); first = first.getNext(); helper.setNext(first); } System.out.printf("最后留在圈中的节点 %d \n" ,first.getNo()); } } class Node { private int no; private Node next; public Node (int no) { this .no = no; } public int getNo () { return no; } public void setNo (int no) { this .no = no; } public Node getNext () { return next; } public void setNext (Node next) { this .next = next; } }