数据结构
一、概念和术语
- 数据结构是一门研究非数值计算的程序的操作对象,以及他们之间关系和操作问题的学科。
数据:
数据元素:
数据项:
数据对象:(是性质相同的数据元素对象集合,是数据的子集)
数据结构:是相互之间存在一种或多种特定关系的数据元素集合。
2.逻辑结构和物理结构
- 逻辑结构 顾名思义是指数据对象中数据元素之间的相互关系 (实例化类的相互关系)
- 集合结构 同一个集合 没有什么关系 终生平等
- 线性结构 一一对应
- 树形结构 各个数据元素之间存在一种多对多的关系
- 图形结构 数据元素是多对多的关系
- 物理结构 (内存中的排列)
总结
二、算法
以后再填
三、线性表List
1.顺序存储结构
线性表:0个或者多个数据元素组成的有序序列
最简单的一种数据结构 线性表:
头结点只有且仅有一个直接后继,尾结点有且只有一个直接前驱
数组是一种典型的线性表 、顺序存储结构
线性表:
存储位置计算(只适用于顺序存储)
第i个元素下1位地址计算书
loc(ai + 1) = loc(ai) * (一个元素所需要的存储空间 C中可以使用sizeof()来获取)
ADT 线性表 (list)
operation:(基本操作)
initList();初始化链表
listEmpty(); 若线性表为空返回
clearList() 清空线性表
getElem() 返回第i个元素的值
*localElem()查找一个元素
*listInsert()在第i个位置插入新元素
*listDelete() 删除线性表第i个元素,并获取删除的值
listLength()获取线性表长度
以上是最基本的操作,但也完全足够,复杂的操作我们可以将他们复合在一起实现
实现:基于Java
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
| import javax.xml.crypto.Data;
class DataType{ int value; public DataType() { } public DataType(int value) { this.value = value; } }
class List{ DataType[] elem; static int length = 0;
boolean initList(int count){ try{ this.elem = new DataType[count]; }catch (Exception e){ System.out.println("内存分配失败"); }
return true; }
void destroyList(){ this.elem = null; this.length = 0; }
int getLength(){ return this.length; }
boolean isEmpty(){ if (this.length == 0) return true; else return false; }
DataType getElem(int i){
if (i > this.length || i < 1) return null; System.out.println("返回元素的值为"+this.elem[i - 1].value); return this.elem[i - 1]; }
int locateElem(DataType f){ if (this.isEmpty()) return -1; else for (int i = 0; i < this.length; i++) { if (f.value == this.elem[i].value) return i; } return -1; }
boolean listInsert(DataType insert){ return listInsert(insert, this.length + 1); } boolean listInsert(DataType insert, int index){ if (index < 1 || this.length >= 20) return false;
for (int i = length - 1; i >= index - 1; i--) { this.elem[i] = this.elem[i - 1]; } this.elem[index - 1] = insert; this.length ++; return true; }
boolean deleteElem(int index){ return deleteElem(index, null); } boolean deleteElem(int index, DataType dele_temp){ if (index < 1 || index > this.length) return false; else { dele_temp = this.elem[index - 1]; for (int i = index - 1; i < this.length - 1; i++) { this.elem[i] = this.elem[i + 1]; } } this.length --; return true; }
boolean showElem(){ if (this.isEmpty()) return false; for (int i = 0; i < this.length; i++) { System.out.print(this.elem[i].value+" "); } System.out.println(""); return true; }
}
public class Main{ public static void main(String[] args) { List l = new List(); l.initList(20); DataType[] data = new DataType[20];
for (int i = 0; i < 20; i++) { data[i] = new DataType(); data[i].value = i + 1; if (l.listInsert(data[i])) System.out.println("插入成功");
} System.out.println("插入了"+l.getLength()+"个数据元素");
l.showElem(); int b = l.locateElem(new DataType(22)); if (b != -1) System.out.println("找到了值为这个元素"); else System.out.println("没有找到");
l.deleteElem(10); l.showElem(); System.out.println("当前长度"+l.getLength());
l.listInsert(new DataType(10), 10); l.showElem(); System.out.println("当前长度"+l.getLength()); if (!l.listInsert(new DataType(20))) System.out.println("插入失败");
l.destroyList(); l.showElem(); } }
|
- 以上顺序存储的线性表依旧存在很多缺陷
- Java方法没有默认值,因此实现默认值用的是重载方法实现
- 基本实现了增删改查等功能
- 本质还是引用传递(Java是值传递),所以各元素内存值并不是连续的,但无伤大雅思想是对的
优点:
- 随机去表中任意元素 O(1)
- 存储密度大(节点本身所占存储量/节点结构所占的存储量)
缺点:
- 增加删除元素 O(n) 最坏的情况就是对表头操作
- 浪费存储空间
- 数据元素最大值固定,属于静态存储形式
2.链式存储
对比顺讯存储结构:
优点:
- 长度不定义 可以扩容
- 插入删除结点 O(1)
- 内存地址不连续
缺点:
2.1单链表
对顺序存储结构的链表优化,可以自由扩充链表
Node (单链表)
Data:数据域
Ptr:指针域
- 每个数据元素有数据域和指针域(引用)
- 通过一个头指针来指定线性表也叫作单链表(有头结点(为空时头指正指向null),无头结点 都一样(头结点其指针域null))
- 最后一个节点指向null,头结点指向链表的第一个节点
- 逻辑结构 和 物理结构不在相等
Java 实现:
1 2 3 4 5 6 7 8
| class ElemType{ int value;
public ElemType() { } public ElemType(int value) { this.value = value; }
|
static int length; 可不定义
需要自定义getLength() 方法 每次获取时间复杂度为O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Node { ElemType data; Node next = null;
static int length = 0;
public Node() { } public Node(ElemType data){ this.data = data; } public Node(ElemType data, Node next) { this.data = data; this.next = next; } }
|
基本方法
- 基于Java类实现,其他语言需要传入头指针参数 this改为head
isEmpty()检查是否为空链
1 2 3 4 5 6
| boolean isEmpty() { if (this.next == null) return true; else return false; }
|
getLength()获取链表长度
1 2 3
| int getLength() { return this.length; }
|
initLinkList() 初始化化链表
- Java 没有默认值,用重载方法 为其添加默认值
- 这里使用的头插法 初始化链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Node initList() { int count = 1; return initList(); }
Node initList(int count) { if (count < 1) return null; Node head = this;
for (int i = 0; i < count; i++) { head.next = new Node(new ElemType(i + 1), head.next); head = head.next; this.length++; } return this; }
|
- initLinkList_end() 尾插法实现的链表初始化
1 2 3 4 5 6 7 8 9 10 11 12 13
| boolean initList_end(int count) { this.destoryList();
Node head = this; for (int i = 0; i < count; i++) { head.next = new Node(new ElemType(i + 1), head.next); head = head.next; this.length++; } return true;
}
|
- insertList(int index, Node data) 插入一个元素 在index位置前 插入一个结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| boolean insertList(int index, Node data) { if (index > this.getLength() || index < 1) return false;
Node head = this.next; int count = 1;
while (head != null && count < index - 1) { head = head.next; count++; } data.next = head.next; head.next = data; this.length++; return true;
}
|
- getNode(int index ) 获取下标为index 的数据元素 并返回其地址(引用)
1 2 3 4 5 6 7 8 9 10 11 12
| Node getNode(int index){ if (index < 1 || index > this.length) return null; Node ret_val = this; int i = 0; while (ret_val != null || i < index){ ret_val = ret_val.next; i++; } return ret_val.next; }
|
- 删除一个结点 deleteNode() 返回成功与否
可改返回删除Node,方便回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| boolean deleteNode(int index){ if (index < 1 || index > this.length) return false;
int i = 0; Node head = this; while (head != null && i < index - 1){ head = head.next; i++; } head.next = head.next.next; this.length --;
return true; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int locaList(ElemType loacl){ return this.locaList(loacl, null); } int locaList(ElemType local, ElemType value){ Node head = this.next; int index = 0;
while (head != null) { if (local.value == head.data.value){ value.value = head.data.value; return index; } head = head.next; index ++; } return -1; }
|
2.2静态链表
数组实现的链表 statickLinkList
结点类:
1 2 3 4 5 6 7 8 9 10 11 12
| public class Node {
ElemType arrNode; int cur;
public Node(){}
public Node(int i){ this.arrNode = new ElemType(i); } }
|
数据域:
1 2 3 4 5 6 7 8 9 10 11 12
| public class ElemType {
int data;
public ElemType(){ this.data = 0; } public ElemType(int vlaue) { this.data = vlaue; } }
|
init
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static Node[] initStaticLinkList(Node[] sl, int length){ for (int i = 0; i < length - 1; i++) { sl[i] = new Node(i + 0); sl[i].cur = i + 1; }
sl[length - 2].cur = -1; sl[length - 1] = new Node(); sl[length - 1].cur = 0; System.out.println("初始化成功"); return sl; }
|
insertStaticLinkList
插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public boolean insertStaticLinkList(Node[] sl ,int index, ElemType insert){ int i = malloc(sl); if (i == -1) return false;
int head = sl.length - 1; int count = 0; while (count < index && sl[head].cur != 0){ head = sl[head].cur; count ++; } sl[0].cur = sl[i].cur;
sl[i].cur = sl[head].cur; sl[head].cur = i;
return true; }
|
delectNode
删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public boolean deletNode(Node[] ls, int index){ if (isEmpty(ls)) return false;
int head = ls.length - 1; int count = 0;
while (ls[head].cur != 0 && count < index){ head = ls[head].cur; count ++; } if (ls[head].cur <= 0) return false;
int next = ls[head].cur; ls[head].cur = ls[ls[head].cur].cur;
ls[next].cur = ls[0].cur; ls[0].cur = next; return true; }
|
localNode
查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int localElem(Node[] sl, ElemType getElem){ if (this.isEmpty(sl)) { System.out.println("链表为空"); return 0; } int count = 0;
int head = sl.length - 1; while (sl[head].cur != 0){ head = sl[head].cur; if (sl[head].arrNode.data == getElem.data) return count + 1; count ++; }
return 0; }
|
getLength() & destoryList() & isEmpty()
基本方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public int getLength(Node[] sl){ return sl.length; } public void destroyStaticLinkList(Node[] sl){ this.initStaticLinkList(sl, 20); }
public boolean isEmpty(Node[] sl){ if (sl[sl.length - 1].cur == 0) return true; return false; }
|
2.2双链表
Node (单链表)
数据域: 自定义数据类型
指针域:
Ptr: next
Ptr2: prior
数据域:
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
| package Demo1;
public class DuLinkList { ElemType data;
DuLinkList prior; DuLinkList next;
public DuLinkList(){ this.data = new ElemType(0); } public DuLinkList(ElemType data) { this.data = data; }
}
class ElemType{
int value;
public ElemType(){} public ElemType(int value){ this.value = value; } }
|
基本方法实现:
- getLength
- isEmpty
- destroyList
- inser, delect, local,getElem
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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
| package Demo1;
public class Test { public static void main(String[] args) { DuLinkList head; head = initDuLinkList(); System.out.println("链表初始化成功");
for (int i = 0; i < 10; i++) { insertDuLinkList(head, i + 1,new ElemType(i + 1)); }
System.out.println("当前链表长度" + getLength(head)); show(head);
System.out.println("在链表最后插入一个数据"); insertDuLinkList(head, 11,new ElemType(99));
System.out.println("当前链表长度" + getLength(head)); show(head);
System.out.println("在链表中查找99这个元素"); int serch = locaNode(head, new ElemType(99)); if (serch > 0) System.out.println("在第"+serch+"个位置找到该元素"); else System.out.println("No find");
System.out.println("获得第11个元素值"); ElemType data = getElem(head, 11); if (data != null) System.out.println("get value = " + data.value);
System.out.println("删除第11个元素"); delectNode(head, 11); System.out.println("删除后的链表");
System.out.println("当前链表长度" + getLength(head)); show(head);
destoryDuLinkList(head); if(isEmpty(head)) System.out.println("当前链表为空"); }
public static DuLinkList initDuLinkList(){ DuLinkList lists = new DuLinkList(); lists.next = lists; lists.prior = lists;
return lists; }
public static boolean isEmpty(DuLinkList head){ if (head.next == head) return true; return false; }
public static int getLength(DuLinkList list){ if (!isEmpty(list)){ DuLinkList head = list; int length = 0;
while (head.next != list){ head = head.next; length++; } return length; }else return 0; }
public static boolean insertDuLinkList(DuLinkList list, int index, ElemType data){ if (index <= 0) return false;
DuLinkList head = list;
int count = 0; while (count < index - 1 && head.next != list){ head = head.next; count++; } DuLinkList New = new DuLinkList(data); New.prior = head; New.next = head.next; head.next.prior = New; head.next = New;
return true; }
public static void show(DuLinkList list){ if (isEmpty(list)) return; DuLinkList head = list; int count = 0; while (head.next != list){ head = head.next; count ++; System.out.print("第"+count+"个元素值:"); System.out.print(head.data.value + " "); } System.out.println(); } public static boolean delectNode(DuLinkList list, int index){ return delectNode(list, index, null); } public static boolean delectNode(DuLinkList list, int index, ElemType get_elem){ if (getLength(list) < index || index < 1) { System.out.println("下标不合法"); return false; }
DuLinkList head = list; int count = 0; while (head.next != list && count < index -1){ head = head.next; count++; }
head.next = head.next.next; head.next.prior = head;
return true; }
public static int locaNode(DuLinkList list,ElemType serch_elem){ return locaNode(list, serch_elem,null); } public static int locaNode(DuLinkList list, ElemType serch_elem,ElemType get_data){ if (isEmpty(list)){ System.out.println("当前链表为空"); return -1; }
DuLinkList head = list; int ret = 0; while (head.next != list){ head = head.next; ret++; if (head.data.value == serch_elem.value ) return ret; } return 0; }
public static ElemType getElem(DuLinkList list, int index){ if (index > getLength(list) || index < 1) { System.out.println("下标越界"); return null; } DuLinkList head = list; int count = 0;
while(head.next != list && count <= index){ count++; head = head.next; } if (head == list) return null; else return head.data; }
public static boolean destoryDuLinkList(DuLinkList list){ DuLinkList head = list.next; DuLinkList temp = head;
while (head != list){ temp = head.next; head = null; head = temp; }
list.next = list; list.prior = list; return true; } }
|
2.3循环链表
在线性表基础上 next -> null 改为指向头结点
重构方法中null 结束遍历的循环 改为头结点
优点:
以下实现两个链表组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void listConnet(LoopNode list, LoopNode list2){ if (isEmpty(list) || isEmpty(list2)) return; LoopNode head = list; while (head.next != list){ head = head.next; } head.next = list2.next; head = list2; while (head.next != list2) head = head.next; head.next = list; }
|
总结
两种结构是互补的,没有哪一种存储方式最优,相对不同问题使用不同结构才是正解
链式存储结构:
- 优点:
- 缺点:
- 失去了顺序结构的随机读取O(n)
- 有一部分指针域空间开销
顺序存储结构:
四、栈Stack
栈
栈是一种线性表,线性表特性他都有
顺序存储结构实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package Demo1;
public class StackDataType { int value;
public StackDataType() { this.value = 0; } public StackDataType(int value) { this.value = value; } }
|
Operation:
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
| package Demo1;
public class Operation { private static Demo1.Stack s;
public static boolean initStack(Stack s){ s.top = 0; return true; }
public static boolean push(Stack s, StackDataType data){ if (s.top == s.data.length){ System.out.println("栈溢出"); return false; } s.data[s.top] = data; s.top++; return true; }
public static boolean isEmpty(Stack s){ if (s.top == 0) return true; else return false; }
public static StackDataType pop(Stack s){ s.top--; return s.data[s.top]; }
public static void show_stack(Stack s){ System.out.println("栈:"); for (int i = 0; i < s.data.length; i++) { if (i != s.top) System.out.print(" --> "); else { System.out.print(" Top "); break; } } System.out.println(""); for (int i = 0; i < s.top; i++) { System.out.printf("%5s", s.data[i].value); } } }
|
双向栈
- 相比栈 多一个栈顶,一个栈顶位于0, 另一头栈顶位于MAXSIZE +1
- 用数组更好实现
- 判断空栈和满栈
- 空 Top1 = 0 && Top2 = MAXSIZE -1
- 满栈Top1 + 1 = Top2
基本操作
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
| package Demo1;
public class Operation { private static Demo1.Stack s;
public static boolean initStack(Stack s){ s.top1 = 0; s.top2 = s.data.length - 1; return true; }
public static boolean push(Stack s, StackDataType data, int stack_number){ if (s.top1 + 1 == s.top2){ System.out.println("栈溢出"); return false; }
if (stack_number == 1){ s.data[s.top1] = data; s.top1++; return true; }else{ s.data[s.top2] = data; s.top2--; return true; } }
public static boolean isEmpty(Stack s){ if (s.top1 == 0 && s.top2 == s.data.length - 1) return true; else return false; }
public static StackDataType pop(Stack s, int stack_number){ if (isEmpty(s)) return null; if (stack_number == 1){ s.top1 --; return s.data[s.top1+1]; }else { s.top2--; return s.data[s.top2-1]; } }
public static void show_stack(Stack s){ System.out.println("栈:"); for (int i = 0; i < s.data.length; i++) { if (i != s.top1) System.out.print(" --> "); else { System.out.print(" Top "); break; } } for (int i = s.top2; i < s.data.length; i++) { if (i != s.top2) System.out.print(" <-- "); else { System.out.print(" Top2 "); } }
System.out.println(""); for (int i = 0; i < s.top1; i++) { System.out.printf("%5s", s.data[i].value); }
System.out.print(" "); for (int i = s.top2 + 1; i < s.data.length; i++) { System.out.printf("%5s", s.data[i].value); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package Demo1;
public class Test { public static void main(String[] args) { Stack stack = new Stack(); Operation.initStack(stack);
for (int i = 0; i < 5; i++) { Operation.push(stack, new StackDataType(i + 1), 1); } for (int i = 0; i < 5; i++) { Operation.push(stack, new StackDataType(i + 1), 2); } Operation.push(stack, new StackDataType(22), 2);
Operation.show_stack(stack); } }
|
链表节点定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package Demo1;
public class Stack { StackDataType[] data; int top1; int top2; public Stack(){ this.data = new StackDataType[20]; this.top1 = -1; } public Stack(int n) { this.data = new StackDataType[n]; }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| package Demo1;
public class StackDataType { int value;
public StackDataType() { this.value = 0; } public StackDataType(int value) { this.value = value; } }
|
队列
- 和栈对立的一种线性结构
- First in First out 先进先出
- 插入表尾,出表表头
- 同样有顺序结构和链式结构
数组实现:
难点用数组实现队列的反复重用,链式存储结构就没这么复杂
五、字符串匹配String
朴素字符串匹配
母串abeabcabce
子串为abcabce
最坏的情况下O(n-m)*n
分析一下我们就能得出 每次匹配母串字符 需要一个一个的对比 其中不乏有重复的对比(母串每次与子串匹配如果不相同会回溯至开头下一个位置 而子串是回溯到0) 下面的各种算法就是裁剪掉不必要的回溯
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
| public class _String {
public void comp(char[] str, char[] str2){ int strlength = str.length; int str2lengh = str2.length;
int i = 0; int j = 0;
while(i < strlength){ j = i; System.out.print("运行第"); System.out.println(i);
int k = 0; for (; k < str2lengh;) { if (str[i] == str2[k]) k++; else break; } if (k == str2lengh) { System.out.println("字符串匹配"); return; } i = j + 1; } return; }
public static void main(String[] args) {
char[] ary = { 'a','a','a','a','a','a','a','a','a','a','a' }; char[] str = { 'a', 'b' }; _String s = new _String();
s.comp(ary, str); } }
|
KMP 字符串匹配
相对于朴素的字符串匹配,KMP算法 会优化不必要的回溯
- 依据子串的字符串重复率构建 一个next数组 (前缀 和 后缀的相似程度) 优化匹配串不必要的回溯
求abcabf
的next数组 -> 011123
推导next数组 第一步
在网上看到不少实现数组下标都是从1开始的,我习惯从0开始
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 int[] get_next(char[] str){ int[] next = new int[str.length];
int i = 1; int j = 0;
next[0] = -1;
while (i < str.length){ if(str[j] == str[i] || j-1 == -1) { j ++; i ++; next[i - 1] = j - 1; }else{ j = next[j]; }
} return next;
}
|
i 表示子串后缀字符下标 j表示前缀字符下标
思路:
后缀位置不断后移 判断是否与前缀字符相同 相同就一起地址比对下一个位置,不相同就回溯j值 一直不相同就回溯到0 继续匹配
abcdef
得到next 数组为011111
next数组中的值就是回溯的依据 (这里我改成了-10000)
KMP 算法
有了next 这个数组 我们就可以使用这个数组来帮助我们减少不必要的回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public int index_Kpm(char[] str, char[] str2, int pos){ int i; int j;
i = pos ; j = -1; int[] next = this.get_next(str2); while(i < str.length && str2.length > j){ if (j == -1 || str[i] == str2[j]){ i++; j++; }else j = next[j]; } if (j == str2.length) return i - str2.length; else return -1; }
|
KMP优化
母串aaaabcd
匹配子串aaaac
aaaac next 数组 结果为01231
当i = 4 j = 4 时字符 b != a 但是字符串中会不停对比回溯 直到j = 0 从头开始匹配,其实这部分回溯我们也可以裁剪掉只需在求next数组时添加一个 如果后缀串等于前缀字符 我们就把偏移位置 和前缀的偏移位置一样 如果不相等正常执行next 数组求值即可
只需要更改next 数组 让前缀字符 等于后缀字符时 母串[i] == 子串[j] 时 next 相应位置的next[i] = next[j]即可
next -> 01234
优化后结果 next -> 01114
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
| public int[] get_nextval(char[] str){ int[] next = new int[str.length];
int i = 0; int j = -1;
next[0] = -1;
while (i < str.length - 1){ if( j == -1 || str[j] == str[i]) { j ++; i ++; if (str[j] == str[i]) next[i] = next[j]; else next[i] = j; }else{ j = next[j]; }
} return next; }
|
字符串匹配没有图是真的难受后期把图补上会更好理解一点
总结
KMP算法 优化了字符串匹配的一些特殊情况,适用于子串中有很高重复率的字符 才能体现KMP算法优势,否则效率并没有比传统字符串匹配高多少
六、树Tree
概念
学到树形结构了,先看看这又臭又长的定义
Tree 是n≥0有限集合 , n=0为空,非空数只有一个根节点,同深度的子树之间没有任何关系
度(Degree):
度等于一个结点的拥有的子树或者孩子
leaf 叶结点或者叫终端结点 度为0
非终端结点或称为者分支结点,除根结点之外,分支结点也称为内部结点。
数的度 是结点度的最大值
结点关系
线性结构是一对一,树形结构是一对多,同一个双亲结点的孩子互称兄弟,任意子树的根节点是所有结点的祖先
结点层次Level
根为第一层 往下递增。
双亲在同一层,互称堂兄弟。
数中结点最大深度为该树的深度
顺序
有序树:每个结点有严格的顺序之分 ,左子树右子树 不能互换,否则就是一个无序树
存储结构
两者都能采用但是对于顺序结构 操作有点不同
顺序存储结构:对于数顺序结构并不能直接表现树存储关系,简单的顺序存储不能满足需要
链式存储结构: 同顺序存储结构一样