应用程序后在那个的数据大致有四种基本的逻辑结构:
对于数据不同的逻辑结构,计算机在物理磁盘上通常有两种屋里存储结构
本篇博文主要讲的是线性结构,而线性结构主要是线性表,非线性结构主要是树和图。 线性表的基本特征:
1.线性表的顺序存储结构:是指用一组地址连续的存储单元一次存放线性表的元素。为了使用顺序结构实现线性表,程序通常会采用数组来保存线性中的元素,是一种随机存储的数据结构,适合随机访问。java中ArrayList类是线性表的数组实现。
1 import java.util.Arrays;
2 public class SequenceList<T>
3 {
4 private int DEFAULT_SIZE = 16;
5 //保存数组的长度。
6 private int capacity;
7 //定义一个数组用于保存顺序线性表的元素
8 private Object[] elementData;
9 //保存顺序表中元素的当前个数
10 private int size = 0;
11 //以默认数组长度创建空顺序线性表
12 public SequenceList()
13 {
14 capacity = DEFAULT_SIZE;
15 elementData = new Object[capacity];
16 }
17 //以一个初始化元素来创建顺序线性表
18 public SequenceList(T element)
19 {
20 this();
21 elementData[0] = element;
22 size++;
23 }
24 /**
25 * 以指定长度的数组来创建顺序线性表
26 * @param element 指定顺序线性表中第一个元素
27 * @param initSize 指定顺序线性表底层数组的长度
28 */
29 public SequenceList(T element , int initSize)
30 {
31 capacity = 1;
32 //把capacity设为大于initSize的最小的2的n次方
33 while (capacity < initSize)
34 {
35 capacity <<= 1;
36 }
37 elementData = new Object[capacity];
38 elementData[0] = element;
39 size++;
40 }
41 //获取顺序线性表的大小
42 public int length()
43 {
44 return size;
45 }
46 //获取顺序线性表中索引为i处的元素
47 public T get(int i)
48 {
49 if (i < 0 || i > size - 1)
50 {
51 throw new IndexOutOfBoundsException("线性表索引越界");
52 }
53 return (T)elementData[i];
54 }
55 //查找顺序线性表中指定元素的索引
56 public int locate(T element)
57 {
58 for (int i = 0 ; i < size ; i++)
59 {
60 if (elementData[i].equals(element))
61 {
62 return i;
63 }
64 }
65 return -1;
66 }
67 //向顺序线性表的指定位置插入一个元素。
68 public void insert(T element , int index)
69 {
70 if (index < 0 || index > size)
71 {
72 throw new IndexOutOfBoundsException("线性表索引越界");
73 }
74 ensureCapacity(size + 1);
75 //将index处以后所有元素向后移动一格。
76 System.arraycopy(elementData , index , elementData
77 , index + 1 , size - index);
78 elementData[index] = element;
79 size++;
80 }
81 //在线性顺序表的开始处添加一个元素。
82 public void add(T element)
83 {
84 insert(element , size);
85 }
86 //很麻烦,而且性能很差
87 private void ensureCapacity(int minCapacity)
88 {
89 //如果数组的原有长度小于目前所需的长度
90 if (minCapacity > capacity)
91 {
92 //不断地将capacity * 2,直到capacity大于minCapacity为止
93 while (capacity < minCapacity)
94 {
95 capacity <<= 1;
96 }
97 elementData = Arrays.copyOf(elementData , capacity);
98 }
99 }
100 //删除顺序线性表中指定索引处的元素
101 public T delete(int index)
102 {
103 if (index < 0 || index > size - 1)
104 {
105 throw new IndexOutOfBoundsException("线性表索引越界");
106 }
107 T oldValue = (T)elementData[index];
108 int numMoved = size - index - 1;
109 if (numMoved > 0)
110 {
111 System.arraycopy(elementData , index+1
112 , elementData, index , numMoved);
113 }
114 //清空最后一个元素
115 elementData[--size] = null;
116 return oldValue;
117 }
118 //删除顺序线性表中最后一个元素
119 public T remove()
120 {
121 return delete(size - 1);
122 }
123 //判断顺序线性表是否为空表
124 public boolean empty()
125 {
126 return size == 0;
127 }
128 //清空线性表
129 public void clear()
130 {
131 //将底层数组所有元素赋为null
132 Arrays.fill(elementData , null);
133 size = 0;
134 }
135 public String toString()
136 {
137 if (size == 0)
138 {
139 return "[]";
140 }
141 else
142 {
143 StringBuilder sb = new StringBuilder("[");
144 for (int i = 0 ; i < size ; i++ )
145 {
146 sb.append(elementData[i].toString() + ", ");
147 }
148 int len = sb.length();
149 return sb.delete(len - 2 , len).append("]").toString();
150 }
151 }
152 }
2.线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素。 链表又可分为:
下面给出线性表双向链表的实现:java中LinkedList是线性表的链式实现,是一个双向链表。
1 public class DuLinkList<T>
2 {
3 //定义一个内部类Node,Node实例代表链表的节点。
4 private class Node
5 {
6 //保存节点的数据
7 private T data;
8 //指向上个节点的引用
9 private Node prev;
10 //指向下个节点的引用
11 private Node next;
12 //无参数的构造器
13 public Node()
14 {
15 }
16 //初始化全部属性的构造器
17 public Node(T data , Node prev , Node next)
18 {
19 this.data = data;
20 this.prev = prev;
21 this.next = next;
22 }
23 }
24 //保存该链表的头节点
25 private Node header;
26 //保存该链表的尾节点
27 private Node tail;
28 //保存该链表中已包含的节点数
29 private int size;
30 //创建空链表
31 public DuLinkList()
32 {
33 //空链表,header和tail都是null
34 header = null;
35 tail = null;
36 }
37 //以指定数据元素来创建链表,该链表只有一个元素
38 public DuLinkList(T element)
39 {
40 header = new Node(element , null , null);
41 //只有一个节点,header、tail都指向该节点
42 tail = header;
43 size++;
44 }
45 //返回链表的长度
46 public int length()
47 {
48 return size;
49 }
50
51 //获取链式线性表中索引为index处的元素
52 public T get(int index)
53 {
54 return getNodeByIndex(index).data;
55 }
56 //根据索引index获取指定位置的节点
57 private Node getNodeByIndex(int index)
58 {
59 if (index < 0 || index > size - 1)
60 {
61 throw new IndexOutOfBoundsException("线性表索引越界");
62 }
63 if (index <= size / 2)
64 {
65 //从header节点开始
66 Node current = header;
67 for (int i = 0 ; i <= size / 2 && current != null
68 ; i++ , current = current.next)
69 {
70 if (i == index)
71 {
72 return current;
73 }
74 }
75 }
76 else
77 {
78 //从tail节点开始搜索
79 Node current = tail;
80 for (int i = size - 1 ; i > size / 2 && current != null
81 ; i++ , current = current.prev)
82 {
83 if (i == index)
84 {
85 return current;
86 }
87 }
88 }
89 return null;
90 }
91 //查找链式线性表中指定元素的索引
92 public int locate(T element)
93 {
94 //从头节点开始搜索
95 Node current = header;
96 for (int i = 0 ; i < size && current != null
97 ; i++ , current = current.next)
98 {
99 if (current.data.equals(element))
100 {
101 return i;
102 }
103 }
104 return -1;
105 }
106 //向线性链式表的指定位置插入一个元素。
107 public void insert(T element , int index)
108 {
109 if (index < 0 || index > size)
110 {
111 throw new IndexOutOfBoundsException("线性表索引越界");
112 }
113 //如果还是空链表
114 if (header == null)
115 {
116 add(element);
117 }
118 else
119 {
120 //当index为0时,也就是在链表头处插入
121 if (index == 0)
122 {
123 addAtHeader(element);
124 }
125 else
126 {
127 //获取插入点的前一个节点
128 Node prev = getNodeByIndex(index - 1);
129 //获取插入点的节点
130 Node next = prev.next;
131 //让新节点的next引用指向next节点,prev引用指向prev节点
132 Node newNode = new Node(element , prev , next);
133 //让prev的next指向新节点。
134 prev.next = newNode;
135 //让prev的下一个节点的prev指向新节点
136 next.prev = newNode;
137 size++;
138 }
139 }
140 }
141 //采用尾插法为链表添加新节点。
142 public void add(T element)
143 {
144 //如果该链表还是空链表
145 if (header == null)
146 {
147 header = new Node(element , null , null);
148 //只有一个节点,header、tail都指向该节点
149 tail = header;
150 }
151 else
152 {
153 //创建新节点,新节点的pre指向原tail节点
154 Node newNode = new Node(element , tail , null);
155 //让尾节点的next指向新增的节点
156 tail.next = newNode;
157 //以新节点作为新的尾节点
158 tail = newNode;
159 }
160 size++;
161 }
162 //采用头插法为链表添加新节点。
163 public void addAtHeader(T element)
164 {
165 //创建新节点,让新节点的next指向原来的header
166 //并以新节点作为新的header
167 header = new Node(element , null , header);
168 //如果插入之前是空链表
169 if (tail == null)
170 {
171 tail = header;
172 }
173 size++;
174 }
175 //删除链式线性表中指定索引处的元素
176 public T delete(int index)
177 {
178 if (index < 0 || index > size - 1)
179 {
180 throw new IndexOutOfBoundsException("线性表索引越界");
181 }
182 Node del = null;
183 //如果被删除的是header节点
184 if (index == 0)
185 {
186 del = header;
187 header = header.next;
188 //释放新的header节点的prev引用
189 header.prev = null;
190 }
191 else
192 {
193 //获取删除点的前一个节点
194 Node prev = getNodeByIndex(index - 1);
195 //获取将要被删除的节点
196 del = prev.next;
197 //让被删除节点的next指向被删除节点的下一个节点。
198 prev.next = del.next;
199 //让被删除节点的下一个节点的prev指向prev节点。
200 if (del.next != null)
201 {
202 del.next.prev = prev;
203 }
204 //将被删除节点的prev、next引用赋为null.
205 del.prev = null;
206 del.next = null;
207 }
208 size--;
209 return del.data;
210 }
211 //删除链式线性表中最后一个元素
212 public T remove()
213 {
214 return delete(size - 1);
215 }
216 //判断链式线性表是否为空链表
217 public boolean empty()
218 {
219 return size == 0;
220 }
221 //清空线性表
222 public void clear()
223 {
224 //将底层数组所有元素赋为null
225 header = null;
226 tail = null;
227 size = 0;
228 }
229 public String toString()
230 {
231 //链表为空链表时
232 if (empty())
233 {
234 return "[]";
235 }
236 else
237 {
238 StringBuilder sb = new StringBuilder("[");
239 for (Node current = header ; current != null
240 ; current = current.next )
241 {
242 sb.append(current.data.toString() + ", ");
243 }
244 int len = sb.length();
245 return sb.delete(len - 2 , len).append("]").toString();
246 }
247 }
248 public String reverseToString()
249 {
250 //链表为空链表时
251 if (empty())
252 {
253 return "[]";
254 }
255 else
256 {
257 StringBuilder sb = new StringBuilder("[");
258 for (Node current = tail ; current != null
259 ; current = current.prev )
260 {
261 sb.append(current.data.toString() + ", ");
262 }
263 int len = sb.length();
264 return sb.delete(len - 2 , len).append("]").toString();
265 }
266 }
267 }
线性表的两种实现比较
顺序表:顺序表的存储空间是静态分布的,需要一个长度固定的数组,因此总有部分数组元素被浪费。
链表:链表的存储空间是动态分布的,因此不会空间浪费。但是由于链表需要而外的空间来为每个节点保存指针,因此要牺牲一部分空间。
顺序表:顺序表中元素的逻辑顺序与物理存储顺序是保持一致的,而且支持随机存取。因此顺序表在查找、读取时性能很好。
链表:链表采用链式结构来保存表内元素,因此在插入、删除元素时性能要好