线性结构和非线性结构
数据结构包括:线性结构和非线性结构
- 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系
- 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
- 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
- 线性结构常见的有:数组、队列、链表和栈
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构
稀疏数组
应用场景
编写的五子棋程序中,有存盘退出和续上盘的功能
分析问题:
因为该二维数组的很多值默认是0,因此记录了很多没有意义的数据->稀疏数组
基本介绍
当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组
稀疏数组的处理方法是:
- 记录数组一共有几行几列,有多少个不同的值
- 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模
这个小规模的数组中,第一行表示几行,几列,几个有效数据
除了第一行以外的,第一列表示行,第二列表示列,第三列表示值
通过稀疏数组,达到了压缩的效果
稀疏数组转换过程
二维数组转稀疏数组的思路
- 遍历原始的二维数组,得到有效数据的个数sum
- 根据sum就可以创建稀疏数组
sparseArr int[sum+1][3]
- 将二维数组的有效数组存入稀疏数组
稀疏数组转原始的二维数组的思路
- 想读取稀疏数组的第一行,根据第一行的数据,创建原始的二维数组,比如
chessArr2 = int[11][11]
- 在读取稀疏数组后几行数据,并赋给二维数组即可
代码实现
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
| public class forStudy { public static void main(String[] args) { int chessArr[][] = new int[11][11]; chessArr[1][2] = 1; chessArr[2][3] = 2; System.out.println("原始的二维数组"); for (int[] ints : chessArr) { for (int anInt : ints) { System.out.printf("%d\t",anInt); } System.out.println(); } int sum = 0; for (int i = 0; i < 11; i++) { for (int j = 0; j < 11; j++) { if (chessArr[i][j] != 0){ sum ++; } } } int sparseArr[][] = new int[sum + 1][3]; sparseArr[0][0] = 11; sparseArr[0][1] = 11; sparseArr[0][2] = sum; int count = 0; for (int i = 0; i < 11; i++) { for (int j = 0; j < 11; j++) { if (chessArr[i][j] != 0){ count ++; sparseArr[count][0] = i; sparseArr[count][1] = j; sparseArr[count][2] = chessArr[i][j]; } } } System.out.println(); System.out.println("得到的稀疏数组为----------"); for (int[] ints : sparseArr) { System.out.printf("%d\t%d\t%d\t",ints[0],ints[1],ints[2]); System.out.println(); }
int chessArr2[][] = new int[sparseArr[0][0]][sparseArr[0][1]];
for (int i = 1; i < sparseArr[0][2]+1; i++) { chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2]; } for (int[] ints : chessArr2) { for (int anInt : ints) { System.out.printf("%d\t",anInt); } System.out.println(); } } }
|
练习
要求:
在前面的基础上,将稀疏数组保存在磁盘上,比如map.data
恢复原来的数组时,读取map.data进行恢复
队列
基本介绍
- 队列是一个有序列表,可以用数组或链表来实现
- 遵循先进先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
- 示意图:(使用数组模拟队列示意图)
数组模拟队列
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如基本介绍中的示意图,其中maxSize是该队列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变
思路分析
当我们将数据存入队列时称addQueue
,addQueue的处理需要有两个步骤:思路分析
- 将尾指针往后移:rear+1,当front==rear【空】
- 当尾指针小于队列的最大下标maxSize - 1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize - 1【队列满】
代码实现
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
| import java.util.Scanner;
public class StudyArrayQueue { public static void main(String[] args) { ArrayQueue arrayQueue = new ArrayQueue(3); char key = ' '; boolean loop = true; Scanner scanner = new Scanner(System.in); 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': try { arrayQueue.showQueue(); }catch (Exception e){ System.out.println(e.getMessage()); } break; case 'e': scanner.close(); loop = false; break; case 'a': System.out.println("输出一个数"); int value = scanner.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; case 'h': try { int res = arrayQueue.headQueue(); System.out.println("头部的数据"+res); }catch (Exception e){ System.out.println(e.getMessage()); } break; } } System.out.println("程序退出"); } }
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 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()) { throw new RuntimeException("队列为空"); } 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]; } }
|
问题分析及优化
- 目前数组使用一次就不能用了,没有达到复用的效果
- 将这个数组使用算法,改进成环形数组
数组模拟环形队列
思路分析
front变量的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素
front 的初始值 = 0
rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置。因为希望空出一个空间作为约定,rear的初始值 = 0
当队列满时,条件是(rear + 1) % maxSize = front【满】
当队列为空的条件,rear == front 空
当我们这样分析,队列中有效的数据的个数(rear + maxSize - front) % maxSize
设rear = 1,front = 0,maxSize = 3,此时有效个数为(rear + maxSize - front) % maxSize = (1 + 3 - 0) % 3 = 1,此时有效数据个数为1
我们就可以在原来的队列上修改得到一个环形队列
代码实现
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
| import java.util.Scanner;
public class CircleArrayQueue { public static void main(String[] args) { System.out.println("测试数组模拟环形队列"); CircleArray arrayQueue = new CircleArray(4); char key = ' '; boolean loop = true; Scanner scanner = new Scanner(System.in); 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': try { arrayQueue.showQueue(); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': scanner.close(); loop = false; break; case 'a': System.out.println("输出一个数"); int value = scanner.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; case 'h': try { int res = arrayQueue.headQueue(); System.out.println("头部的数据" + res); } catch (Exception e) { System.out.println(e.getMessage()); } break; } } System.out.println("程序退出"); } }
class CircleArray { private int maxSize; private int front; private int rear; 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 + 1) % maxSize; }
public int getQueue() { if (isEmpty()) { throw new RuntimeException("队列为空,无法获取数据"); } int value = arr[front]; front = (front + 1) % maxSize; return value; }
public void showQueue() { if (isEmpty()) { throw new RuntimeException("队列为空"); } 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()) { throw new RuntimeException("队列为空"); } return arr[front]; } }
|
链表
链表是有序的列表,它在内存的存储方式(实际结构)如下图所示
头指针(head)指向了地址150,150表示a1,a1的next域,也就是下一个地址值是110,110表示着a2的data域,也就是值域,后面都是以此类推
- 链表是以节点的方式来存储
- 每个节点包含data域;next域:指向下一个节点
- 如图:发现链表的各个节点不一定是连续存储
- 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
单链表(带头节点)逻辑结构示意图如下
单向链表
创建示意图
添加(创建)
- 先创建一个head头节点,作用就是表示单链表的头
- 后面我们每添加一个节点,就直接加入到链表的最后遍历
创建和遍历
使用带head头的单向链表实现-水浒英雄排行榜管理
在添加英雄时,直接添加到链表的尾部
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
| package fun.eastwind.linkedList;
public 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.list(); } }
class SingleLinkedList { private HeroNode head = new HeroNode(0,"","");
public void add(HeroNode heroNode){
HeroNode temp = head; while (true){ if (temp.next == null){ break; } temp = 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.next; } } }
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 + '\'' + '}'; } }
|
按顺序插入节点
使用带head头的单向链表实现-水浒英雄排行榜管理
根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
思路分析
需要按照编号的顺序添加
- 首先找到新添加的节点的位置,是通过辅助变量(指针),通过遍历来搞定
- 新的节点.next = temp.next
- 将temp.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
| public 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.addByOrder(heroNode1); singleLinkedList.addByOrder(heroNode4); singleLinkedList.addByOrder(heroNode2); singleLinkedList.addByOrder(heroNode3); singleLinkedList.addByOrder(heroNode3); singleLinkedList.list(); } }
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) { System.out.printf("准备插入的英雄的%d编号已经存在\n", heroNode.no); } else { heroNode.next = temp.next; temp.next = heroNode; } }
|
节点修改
根据no查询对应的节点,找到就进行修改,没找到就继续往下查找next的信息,直到next为null
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
| public 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.addByOrder(heroNode1); singleLinkedList.addByOrder(heroNode4); singleLinkedList.addByOrder(heroNode2); singleLinkedList.addByOrder(heroNode3); singleLinkedList.addByOrder(heroNode3); HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~"); singleLinkedList.update(newHeroNode); singleLinkedList.list(); } }
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.nickName = newHeroNode.nickName; }else{ System.out.println("未找到该节点"); } }
|
删除
思路分析
- 我们先找到需要删除的这个节点的前一个节点temp
- temp.next = temp.next.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
| public void delete(HeroNode newHeroNode){ boolean flag; HeroNode temp = head; while (true){ if (temp.next == null){ System.out.println("链表为空"); return; } if (temp.next.no == newHeroNode.no){ flag = true; break; } temp = temp.next; } if (flag){ temp.next = temp.next.next; }else{ System.out.println("未找到该节点"); } }
|
面试题
求单链表节点的个数
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void getSize(){ HeroNode temp = head.next; int count = 0; while (true){ if (temp == null){ break; } count ++; temp = temp.next; } System.out.println(count); }
|
查找单链表中的倒数第k个节点【新浪面试题】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public HeroNode findLastIIndexNode(HeroNode head,int index){ if (head.next == null) return null; int size = getSize(); int resultNumber = size - index; if (index <= 0 || resultNumber < 0){ return null; } HeroNode temp = head.next; for (int i = 0; i < resultNumber; i++) { temp = temp.next; } return temp; }
|
单链表的反转【腾讯面试题】
reverseHead节点是为了存储反转数据,流程如下:
先使用临时变量备份除了反转节点以外的数据,比如说,我们要翻转当前节点,那么就要从当前节点的下一个节点开始备份,让当前节点赋值反转链表的参数值,这个参数值是已经被反转过的值,如果是第一次,反转链表的值是空,就让当前节点的.next赋值为null,然后将当前节点的值赋值到反转链表中,并后移,从第二次开始,第二次就会备份从二节点以后备份,让当前链表的值的next去指向反转数组之前的一节点,然后将当前链表值赋予反转数组,以此类推,后面的3节点直接赋值反转链表的数据,也就是2、1节点,并赋值反转链表,如果还有4节点,就让4节点指向3、2、1节点,4节点的当前链表直接赋予反转链表,最后链表去指向反转链表的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
| public void reverseHead(){ if (head.next == null || head.next.next == null) return; HeroNode cur = head.next; HeroNode next = null; HeroNode reverseHead = new HeroNode(0,"",""); while (cur != null){ next = cur.next; cur.next = reverseHead.next; reverseHead.next = cur; cur = next; } head.next = reverseHead.next; }
|
从尾到头打印单链表【百度,要求方式1:反向遍历。方式2:Stack栈】
通过栈数据结构,将各个节点压入到栈中,然后利用栈先进后出的特性,实现逆序打印的效果
这里简单说明一下栈的使用
入栈是一个一个的存入,最先存入的栈会在最底下,最后存入的在最上面,取出的时候从上面往下取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import java.util.Stack;
public class TestStack { public static void main(String[] args) { Stack<String> stack = new Stack<>(); stack.add("jack"); stack.add("tom"); stack.add("smith");
while (stack.size() > 0){ System.out.println(stack.pop()); } } }
|
Stack(栈)逆序打印
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void reversePrintTable(){ if (head.next == null){ return; } HeroNode temp = head.next; Stack<HeroNode> nodeStack = new Stack<>(); while (temp != null){ nodeStack.add(temp); temp = temp.next; } while (nodeStack.size() > 0) { System.out.println(nodeStack.pop()); } }
|
合并两个有序的单链表,合并之后的链表依然有序【练习】
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
| public HeroNode mergeHeroNode(HeroNode node1,HeroNode node2){ if (node1.next == null && node2.next == null){ return null; } HeroNode heroNode3 = new HeroNode(0, "", ""); if (node1.next == null){ heroNode3.next = node2; } else if (node2.next == null) { heroNode3.next = node1; }else{ heroNode3.next = node1.next; HeroNode cur2 = node2.next; HeroNode cur3 = heroNode3; HeroNode next = null; while (cur2 != null){ if (cur3.next == null){ cur3.next = cur2; break; } if (cur2.no <= cur3.next.no){ next = cur2.next; cur2.next = cur3.next; cur3.next = cur2; cur2 = next; } cur3 = cur3.next; }
} return heroNode3; }
|
双向链表
思路分析
分析双向链表的遍历,添加,修改,删除的操作思路–>
- 遍历和单链表是一致是,但是双向链表可以从后往前查找
- 添加(默认添加到双向链表的最后)
- 先找到双向链表的最后一个节点
- temp.next = newNode(最后一个节点的下一个节点去指向最新的节点)
- newNode.pre = temp(新节点的前一个节点去指向之前的最后一个节点)
- 修改思路和单向链表是一致的
- 删除
- 因为是双向链表,因此,可以实现自我删除某个节点
- 直接找到要删除的这个节点,比如temp
- temp.pre.next = temp.next(令当前节点的上一个节点去指向为当前节点的下一个节点)
- temp.next.pre = temp.pre(令当前节点的下一个节点去指向当前节点的下一个节点)
- 当删除最后一个节点时,就不需要令当前被删除节点的下一个节点去指向当前被删除节点的下一个节点了,因为为空是不需要进行上一个节点的指向的
代码实现
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
| package fun.eastwind.linkedList;
public class TwoLinkedListDemo { public static void main(String[] args) { TwoLinkedList twoLinkedList = new TwoLinkedList(); TwoNode heroNode1 = new TwoNode(1, "宋江", "及时雨"); TwoNode heroNode2 = new TwoNode(8, "卢俊义", "玉麒麟"); TwoNode heroNode3 = new TwoNode(3, "吴用", "智多星"); TwoNode heroNode4 = new TwoNode(7, "林冲", "豹子头"); twoLinkedList.add(heroNode1); twoLinkedList.add(heroNode2); twoLinkedList.add(heroNode3); twoLinkedList.add(heroNode4);
heroNode4.nickName = "张三"; heroNode4.name = "zhangsan"; twoLinkedList.update(heroNode4); twoLinkedList.del(1); twoLinkedList.del(7); twoLinkedList.del(3); twoLinkedList.del(8); twoLinkedList.list(); } }
class TwoLinkedList{ private TwoNode head = new TwoNode(0, "", "");
public void del(int hNo){ TwoNode temp = head.next; while (temp != null){ if (temp.no == hNo){ temp.pre.next = temp.next; if (temp.next != null){ temp.next.pre = temp.pre; } break; } temp = temp.next; } }
public void update(TwoNode twoNode){ TwoNode temp = head.next; while (temp != null){ if (temp.no == twoNode.no){ temp.name = twoNode.name; temp.nickName = twoNode.nickName; break; } temp = temp.next; } }
public void reversedList(){ TwoNode temp = head; while (temp.next != null){ temp = temp.next; } while (temp != null){ System.out.println(temp); temp = temp.pre; } }
public void list(){ TwoNode temp = head; while (temp != null){ System.out.println(temp); temp = temp.next; } }
public void add(TwoNode newNode){ TwoNode temp = head; while (temp.next != null){ temp = temp.next; } temp.next = newNode; newNode.pre = temp; }
}
class TwoNode { public int no; public String name; public String nickName; public TwoNode next; public TwoNode pre;
public TwoNode(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 + '\'' + '}'; } }
|
环形链表
环形链表涉及到了一个约瑟夫问题
约瑟夫问题(Josephu问题):设编号1,2,….n的n个人围坐一圈,约定编号k(1<=k<=n)的人开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
思路分析
假设n = 5;k=1;m=2;
从1开始往后数m下,那么就是1,2;将2提出队列,再到3时,就是3、4;将4提出队列;依次往下,直到3本身,它自己也是一个环形队列,然后它本身自己出列即可
构建一个单向的环形链表的思路:
- 先创建第一个节点,让first指向该节点,并形成环形
- 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可
创建第一个节点,它的next指向first节点,并让first节点去指向第一个节点,形成第一个环形,而后面每创建一个节点,就调整后面的节点数量-1的next,例如创建第二个节点,就让第一个节点的next去指向第二个节点,并让第二个节点的next去指向first,以此类推
代码实现
遍历环形链表
- 先让一个辅助指针(变量)curBoy,指向first节点
- 然后通过一个while循环遍历该环形链表即可,curBoy.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
| package fun.eastwind.linkedList;
public class CircleSingleLinkedListDemo { public static void main(String[] args) { CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList(); circleSingleLinkedList.add(3); circleSingleLinkedList.list(); } }
class CircleSingleLinkedList{ private Boy first = new Boy(-1); public void add(int number){ if (number < 1){ System.out.println("number的值不正确"); return; } Boy curBoy = null; for (int i = 1; i <= number; i++) { Boy boy = new Boy(i); if (i == 1){ first = boy; first.setNext(first); curBoy = first; } else{ curBoy.setNext(boy); boy.setNext(first); curBoy = boy; } } }
public void list(){ if (first == null){ System.out.println("链表为空"); return; } Boy temp = first; while (true){ System.out.println(temp.getNo()); if (temp.getNext() == first){ break; } temp = temp.getNext(); } } }
class Boy{ private int no; private Boy next;
public Boy(int no) { this.no = no; }
public int getNo() { return no; }
public void setNo(int no) { this.no = no; }
public Boy getNext() { return next; }
public void setNext(Boy next) { this.next = next; } }
|
约瑟夫问题
约瑟夫问题(Josephu问题):设编号1,2,….n的n个人围坐一圈,约定编号k(1<=k<=n)的人开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
tip:用一个不带头节点的循环链表来处理Josephu问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除,算法结束。
思路:
需要创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
在报数前,需要先移动到k-1次这个位置,因为是从k开始报数,假设k = 1,那么移动0个位置即可
当小孩报数时,让first和helper指针同时的移动m - 1次
这时就可以将first指向的小孩节点出圈
first = first.next
helper.next = first
原来的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
| package fun.eastwind.linkedList;
public class CircleSingleLinkedListDemo { public static void main(String[] args) { CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList(); circleSingleLinkedList.add(5); circleSingleLinkedList.countBoy(1,2,5); } }
class CircleSingleLinkedList{ private Boy first = new Boy(-1);
public void countBoy(int startNo,int countNum,int nums){ if (first == null || startNo < 1 || startNo > nums ){ System.out.println("参数输入有误"); return; } Boy helper = first; while (true){ if (helper.getNext() == first){ break; } helper = helper.getNext(); } for (int j = 0; j < startNo - 1; j++) { first = first.getNext(); helper = helper.getNext(); } while (true){ if (helper == first){ break; } for (int j = 0; j < countNum - 1; j++) { first = first.getNext(); helper = helper.getNext(); } System.out.printf("小孩出圈%d\n",first.getNo()); first = first.getNext(); helper.setNext(first); } System.out.println("最后一个节点" + first.getNo()); }
public void add(int number){ if (number < 1){ System.out.println("number的值不正确"); return; } Boy curBoy = null; for (int i = 1; i <= number; i++) { Boy boy = new Boy(i); if (i == 1){ first = boy; first.setNext(first); curBoy = first; } else{ curBoy.setNext(boy); boy.setNext(first); curBoy = boy; } } }
public void list(){ if (first == null){ System.out.println("链表为空"); return; } Boy temp = first; while (true){ System.out.println(temp.getNo()); if (temp.getNext() == first){ break; } temp = temp.getNext(); } } }
class Boy{ private int no; private Boy next;
public Boy(int no) { this.no = no; }
public int getNo() { return no; }
public void setNo(int no) { this.no = no; }
public Boy getNext() { return next; }
public void setNext(Boy next) { this.next = next; } }
|
栈
基本介绍
- 栈的英文是(Stack)
- 栈是一个先进后出(FILO-First In Last Out)的有序列表
- 栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
- 根据栈的定义可知,最先放入栈中的元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除
应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,回到原来的程序中
- 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中
- 表达式的转换与求值
- 二叉树的遍历
- 图形的深度优先(depth – first)搜索法
思路分析(数组模拟栈)
- 使用数组来模拟栈
- 定义一个top来表示栈顶,初始化为-1
- 入栈的操作,当有数据加入到栈时,top++;stack[top] = data;
- 出栈的操作, int value = stack[top];top–,return value
代码实现
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
| package fun.eastwind.stack;
import java.util.Scanner;
public class ArrayStackDemo { public static void main(String[] args) { ArrayStack arrayStack = new ArrayStack(4); String key = ""; boolean loop = true; Scanner scanner = new Scanner(System.in);
while (loop){ System.out.println("s:显示栈"); System.out.println("e:退出程序"); System.out.println("push:入栈"); System.out.println("pop:出栈"); System.out.println("please input your choice"); key = scanner.next(); switch (key){ case "s": arrayStack.list(); break; case "e": scanner.close(); loop = false; break; case "push": System.out.println("please input your number"); int num = scanner.nextInt(); arrayStack.push(num); break; case "pop": try{ int pop = arrayStack.pop(); System.out.println("出栈的数据为" + pop); } catch (Exception e){ System.out.println(e.getMessage()); } break; } } } }
class ArrayStack{ private int maxSize; private int[] stack; private int top = -1;
public boolean isFull(){ return top == maxSize - 1; }
public boolean isEmpty(){ return top == -1; }
public void push(int num){ if (isFull()){ System.out.println("栈已满"); return; } top++; stack[top] = num; }
public int pop(){ if (isEmpty()){ throw new RuntimeException("栈空"); } int val = stack[top]; top--; return val; }
public void list(){ if (isEmpty()){ System.out.println("栈为空"); return; } for (int i = top; i >= 0; i--) { System.out.println(stack[i]); } }
public ArrayStack(int maxSize) { this.maxSize = maxSize; this.stack = new int[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 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
| package fun.eastwind.stack;
import java.util.Scanner; import java.util.Stack;
public class LinkedForStackDemo { public static void main(String[] args) { LinkedStackList linkedStackList = new LinkedStackList(4); String key = ""; boolean loop = true; Scanner scanner = new Scanner(System.in);
while (loop){ System.out.println("s:显示栈"); System.out.println("e:退出程序"); System.out.println("push:入栈"); System.out.println("pop:出栈"); System.out.println("please input your choice"); key = scanner.next(); switch (key){ case "s": linkedStackList.list(); break; case "e": scanner.close(); loop = false; break; case "push": System.out.println("please input your number"); int num = scanner.nextInt(); linkedStackList.push(num); break; case "pop": try{ int pop = linkedStackList.pop(); System.out.println("出栈的数据为" + pop); } catch (Exception e){ System.out.println(e.getMessage()); } break; } } } }
class LinkedStackList { int maxSize;
public LinkedStackList(int maxSize) { this.maxSize = maxSize; }
LinkedStack head = new LinkedStack(-1);
public void list(){ if (isEmpty()){ System.out.println("栈空"); return; } LinkedStack temp = head.next; Stack<LinkedStack> linkedStacks = new Stack<>(); while (temp != null){ linkedStacks.add(temp); temp = temp.next; } int size = linkedStacks.size(); for (int i = 0; i < size; i++) { System.out.println(linkedStacks.pop().val); } }
public int pop() { if (isEmpty()) { throw new RuntimeException("栈空"); } LinkedStack temp = head; while (temp.next.next != null){ temp = temp.next; } int Values = temp.next.val; temp.next = null; return Values; }
public void push(int val) { if (isFull()){ System.out.println("栈满"); return; } LinkedStack temp = head; while (temp.next != null) { temp = temp.next; } temp.next = new LinkedStack(val); }
public boolean isEmpty() { return head.next == null; }
public boolean isFull() { int count = 0; LinkedStack temp = head.next; while (temp != null) { count++; temp = temp.next; } return count == maxSize; } }
class LinkedStack { int val; LinkedStack next;
public LinkedStack(int val) { this.val = val; } }
|
栈实现综合计算器
思路分析
使用栈完成表达式的计算思路
- 通过一个index值(索引),来遍历我们的表达式
- 如果我们发现是一个数字,就直接入数栈
- 如果扫描到发现是一个符号,就分如下情况
- 如果符号栈为空,直接入栈
- 如果发现符号栈有操作符,就进行比较,如果当前的操作符优先级小于或等于栈中的操作符,就需要从数栈中pop出两个数,在符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,然后将当前操作符入符号栈,如果当前操作符的优先级大于栈中的操作符,则直接入栈
- 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行
- 最后在数栈中只有一个数字,就是表达式的结果
代码实现
有缺陷,未实现数字连续减法功能
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 156 157 158 159 160 161 162
| package fun.eastwind.stack;
public class Calculator { public static void main(String[] args) { String expression = "300+2-6-2"; ArrayStack2 numberStack = new ArrayStack2(10); ArrayStack2 operationStack = new ArrayStack2(10); int index = 0 ; while (true){ String keepNum = ""; char val = expression.charAt(index); if (operationStack.isOper(val)){ if(operationStack.isEmpty()){ operationStack.push(val); }else{ int priority = operationStack.priority(val); int stackVal = operationStack.priority(operationStack.peek()); if (priority >= stackVal){ operationStack.push(val); }else{ int pop = operationStack.pop(); int val2 = numberStack.pop(); int val1 = numberStack.pop(); int cal = numberStack.cal(val1, val2, pop); numberStack.push(cal); operationStack.push(val); } } }else{ keepNum += val; while (index + 1 < expression.length() && !operationStack.isOper(expression.charAt(index+1))){ index++; keepNum += expression.charAt(index); } numberStack.push(Integer.parseInt(keepNum));
} index ++; if (expression.length() == index){ break; } } while (true){ int num2 = numberStack.pop(); int num1 = numberStack.pop(); int operation = operationStack.pop(); int cal = numberStack.cal(num1, num2, operation); numberStack.push(cal); if(operationStack.isEmpty())break; } System.out.println(numberStack.pop()); } }
class ArrayStack2{ private int maxSize; private int[] stack; private int top = -1;
public int peek(){ return stack[top]; }
public int priority(int operation){ if (operation == '*' || operation == '/'){ return 1; } else if (operation == '+' || operation == '-') { return 0; }else{ return -1; } }
public boolean isOper(char val){ return val == '+' || val == '-' || val == '*' || val == '/'; } public int cal(int num1,int num2,int operation){ int res = 0; switch (operation){ case '+':res = num1 + num2;break; case '-':res = num1 - num2;break; case '*':res = num1 * num2;break; case '/':res = num1 / num2;break; } return res; }
public boolean isFull(){ return top == maxSize - 1; }
public boolean isEmpty(){ return top == -1; }
public void push(int num){ if (isFull()){ System.out.println("栈已满"); return; } top++; stack[top] = num; }
public int pop(){ if (isEmpty()){ throw new RuntimeException("栈空"); } int val = stack[top]; top--; return val; }
public void list(){ if (isEmpty()){ System.out.println("栈为空"); return; } for (int i = top; i >= 0; i--) { System.out.println(stack[i]); } }
public ArrayStack2(int maxSize) { this.maxSize = maxSize; this.stack = new int[maxSize]; } }
|
前缀、中缀、后缀表达式规则
前缀表达式(波兰表达式)
- 前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前
- 举例说明:(3 + 4) × 5 - 6对应的前缀表达式就是- × + 3 4 5 6
前缀表达式求值
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素和次顶元素),将结果入栈;重复上述过程,直到表达式最左端,最后运算得出的值即为表达式的结果
例如:(3 + 4) × 5 - 6对应的前缀表达式就是- × + 3 4 5 6,针对前缀表达式求值步骤如下:
- 从右至左扫描,将6、5、4、3压入堆栈
- 遇到+运算符因此弹出3和4(3为栈顶元素,4为次顶元素),计算3+4的值,得7,并入栈
- 接着是×运算符,因此弹出7和5,计算7×5 = 35,将35入栈
- 最后是-运算符,计算35-6的值,即29,由此得出结果
中缀表达式
- 中缀表达式就是常见的运算表达式,如(3+4)×5-6
- 中缀表达式的求值是我们最熟悉的,但对计算机来说不好操作,因此,在计算时,通常会将中缀表达式转成其他表达式来操作
后缀表达式
- 后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
- 举例说明:(3 + 4) × 5 - 6对应的后缀表达式就是3 4 + 5 × 6 -
正常的表达式 |
逆波兰表达式 |
a + b |
a b + |
a + (b - c) |
a b c - + |
a + (b - c) * d |
a b c - d * + |
a + d * (b - c) |
a d b c - * + |
a = 1 + 3 |
a 1 3 + = |
后缀表达式的计算机求值
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶两个数,用运算符对它们做相应的计算(次顶元素和栈顶元素),并将结果入栈;重复上述操作,直到表达式的最右端,最后运算得出的值即为表达式的结果
例如:(3 + 4) × 5 - 6对应的后缀表达式就是3 4 + 5 × 6 -,针对后缀表达式求值步骤如下:
- 从左至右扫描,将3和4压入堆栈
- 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算3 + 4的值,得7,再将7入栈
- 将5入栈
- 接下来是×运算符,因此弹出5和7,计算7×5=35,将35入栈
- 将6入栈
- 最后是-运算符,计算35-6的值,即29,由此得出最后结果
逆波兰计算器
- 输入一个逆波兰表达式,使用栈(Stack),计算其结果
- 支持小括号和多位数整数
代码实现
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
| package fun.eastwind.stack;
import java.util.ArrayList; import java.util.List; import java.util.Stack;
public class PolandNotation { public static void main(String[] args) { String suffixExpression = "30 4 + 5 * 6 - "; List<String> listString = getListString(suffixExpression); System.out.println(calculate(listString)); }
public static int calculate(List<String> ls){ Stack<String> stack = new Stack<>(); for (String l : ls) { if (l.matches("\\d+")){ stack.push(l); }else{ int num2 = Integer.parseInt(stack.pop()); int num1 = Integer.parseInt(stack.pop()); int res = 0; if (l.equals("+")){ res = num1 + num2; } else if (l.equals("-")) { res = num1 - num2; } else if (l.equals("*")) { res = num1 * num2; } else if (l.equals("/")) { res = num1 / num2; }else{ throw new RuntimeException("运算符有问题"); } stack.push(res + ""); } } return Integer.parseInt(stack.pop()); }
public static List<String> getListString(String suffixExpression){ String[] split = suffixExpression.split(" "); List<String> list = new ArrayList<>(); for (String ele : split) { list.add(ele); } return list; } }
|
中缀转后缀表达式
思路分析
很显然,后缀表达式适合计算式进行运算,但是人不太容易写出来,尤其是表达式很长的情况下,因此在开发中,需要将中缀表达式转成后缀表达式
具体步骤如下:
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压入s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级(小括号不属于运算符);
- 如果s1为空,或栈顶运算符为左括号”(“,将该运算符直接入栈
- 否则,若优先级比栈顶运算符高,也将运算符压入s1
- 否则,将s1栈顶的运算符弹出并压入s2中,再次与s1中的新栈顶运算符比较
- 遇到括号时:
- 如果是左括号”(“,直接压入s1
- 如果是右括号”)”,依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将括号丢弃
- 重复步骤2至5,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
代码实现
不断的遍历中缀表达式,根据不同的情况,来加入不同的值
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
| public static String computedInfixResult(List<String> list){ Stack<String> stack1 = new Stack<String>(); Stack<String> stack2 = new Stack<String>(); for (String val : list) { if (val.matches("\\d+")){ stack1.push(val); } else{ if(stack2.isEmpty() || stack2.peek().equals("(")){ stack2.push(val); } else if (priority(val) == -1) { if (val.equals("(")) stack2.push(val); else{ while (true){ if (stack2.peek().equals("(")){ stack2.pop(); break; }else { stack1.push(stack2.pop()); } } } } else if (priority(val) >= priority(stack2.peek())){ stack1.push(stack2.pop()); while (!stack2.isEmpty()){ if (priority(val) >= priority(stack2.peek())){ stack2.push(val); }else break; } } } } while (!stack2.isEmpty()){ stack1.push(stack2.pop()); } StringBuilder res = new StringBuilder(); int size = stack1.size(); for (int i = 0; i < size; i++) { res.append(stack1.pop() + " "); } return String.valueOf(res.reverse()); }
public static int priority(String operation){ if (operation.equals("*") || operation.equals("/")){ return 1; } else if (operation.equals("+") || operation.equals("-")) { return 0; }else{ return -1; } }
public static List<String> toInfixExpressionList(String s){ List<String> ls = new ArrayList<>(); int i = 0; String str; char c; do{ if ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) > 57){ ls.add("" + c); i++; }else{ str = ""; while (i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57){ str += c; i++; } ls.add(str); } }while (i < s.length()); return ls; }
|