1, 数组
Q1: 数组我们都很熟悉,那你理解的数组是什么样的呢?它的最主要特点是什么呢?
数组在内存空间上是连续存储 -> 随机访问
某一个下标位置 = 数组的起始位置 + 下标 * 单个数组单元大小
Q2: 为什么数组的索引是一般都是从0开始的呢? 历史遗留问题
"易语言" -> 数组下标从1开始
如果从1开始:
某一个下标位置 = 数组的起始位置 + (下标-1) * 单个数组单元大小
Q3: 为什么数组的效率比链表高?
第一层:
数组在内存空间上是连续存储
链表是非连续存储, 访问要一步一步next向后查找
第二层:
频繁的数据在缓存中置换, 比较消耗硬件开销
2, 链表
2.1链表的分类
链表是由结点构成的
除了尾结点以外每个结点都有一个后继结点
在单链表的基础上, 让尾结点的下一个指向头结点
是在单链表的基础上, 让每一个结点都有前后指向(除了头尾结点)
是在双向链表的基础上, 让头的前一个指向尾结点, 尾的后一个指向头结点
2.2数组和链表的对比
数组和链表的插入,删除,随机访问操作的时间复杂度刚好相反。
① 数组使用的是连续的内存空间,可以利用CPU的高速缓存预读数据。链表的内存空间不是连续的,不能有效预读数据。当然如果数组过大,系统没有足够的连续内存空间,会抛出OOM
(out of memory error)申请程序内存较大,虚拟机无法满足我们。
② 数组的缺点是大小固定,没法动态的调整大小。如果要存储一些对象,如果数组太大,浪费内存空间;如果数组太小,我们需要重新申请一个更大数组,并将数据拷贝过去,耗时。
③如果业务对内存的使用非常苛刻,数组更适合。因为结点有指针域,更消耗内存。而且对链表的频繁插入和删除,会导致结点对象的频繁创建和销毁,有可能会导致频繁的GC活动。
3, 线性表的实现
线性表根据定义 --> 有下标/位序的概念 --> 可以提供根据下标的操作(根据下标的增删改查)
二叉搜索树: 添加, 删除,查找, 修改
举例:实现单链表和双链表:
单链表:
public class MyLinkedList {
private Node head;
private int size;
public boolean add(String str) {
if (isEmpty()){
head = new Node(str, null);
size++;
return true;
}
Node mid = head;
while (mid.next != null){
mid = mid.next;
}
mid.next = new Node(str, null);
size++;
return true;
}
public boolean remove(String str) {
if (isEmpty())throw new RuntimeException("list is empty");
if (str == null){
if (str == head.value){
head = head.next;
size--;
return true;
}
Node mid = head;
while (mid.next != null && str != mid.next.value ){
mid = mid.next;
}
if (mid.next == null)return false;
mid.next = mid.next.next;
size--;
}else {
if (str.equals(head.value)){
head = head.next;
size--;
return true;
}
Node mid = head;
while (mid.next != null && !str.equals(mid.next.value)){
mid = mid.next;
}
if (mid.next == null)return false;
mid.next = mid.next.next;
size--;
}
return true;
}
public boolean contains(String str) {
if (isEmpty())throw new RuntimeException("list is empty");
Node mid = head;
while (mid != null && !str.equals(mid.value)){
mid = mid.next;
}
if (mid == null)return false;
return true;
}
public boolean set(String oldValue, String newValue) {
if (isEmpty())throw new RuntimeException("list is empty");
Node mid = head;
while (mid != null && !oldValue.equals(mid.value)){
mid = mid.next;
}
if (mid == null)return false;
mid.value = newValue;
return true;
}
public boolean add(int index, String str) {
if(index < 0 || index > size) throw new IllegalArgumentException("index is Illegal ");
if (index == 0){
head = new Node(str, head);
size++;
return true;
}
Node mid = head;
int tag = 1;
while (tag != index){
tag++;
mid = mid.next;
}
mid.next = new Node(str, mid.next);
size++;
return true;
}
public String remove(int index) {
if(index < 0 || index >= size) throw new IllegalArgumentException("index is Illegal ");
if (index == 0){
String oldValue = head.value;
head = head.next;
size--;
return oldValue;
}
Node mid = head;
int tag = 1;
while (tag != index){
tag++;
mid = mid.next;
}
String oldValue = mid.next.value;
mid.next = mid.next.next;
size--;
return oldValue;
}
public String get(int index) {
if(index < 0 || index >= size) throw new IllegalArgumentException("index is Illegal ");
Node mid = head;
int tag = 0;
while (tag != index){
tag++;
mid = mid.next;
}
return mid.value;
}
public String set(int index, String newValue) {
if(index < 0 || index >= size) throw new IllegalArgumentException("index is Illegal ");
Node mid = head;
int tag = 0;
while (tag != index){
tag++;
mid = mid.next;
}
String oldValue = mid.value;
mid.value = newValue;
return oldValue;
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
class Node{
String value;
Node next;
public Node(String value, Node next) {
this.value = value;
this.next = next;
}
}
}
双向链表的实现:
public class MyDBLinkedList<T> {
private Node head;
private Node end;
private int size;
public boolean add(T value){
if (isEmpty()){
head = new Node(null, value, null);
end = head;
size++;
return true;
}
end.next = new Node(end, value, null);
end = end.next;
size++;
return true;
}
public boolean remove(T value){
if (isEmpty())throw new RuntimeException("list is empty");
if (value.equals(head.value)){
if (size == 1){
head = null;
end = null;
size = 0;
return true;
} else {
head = head.next;
head.pre = null;
size--;
return true;
}
}
if (value.equals(end.value)){
end = end.pre;
end.next = null;
size--;
return true;
}
Node mid = head;
while (mid.next != null && !value.equals(mid.next.value)){
mid = mid.next;
}
if (mid.next == null) return false;
Node removeNode = mid.next;
removeNode.next.pre = removeNode.pre;
removeNode.pre.next = removeNode.next;
size--;
return true;
}
public boolean add(int index, T value){
if (index < 0 || index > size) throw new IllegalArgumentException("size =" + size + "; index = " + index);
if (index == 0){
if (isEmpty()){
head = new Node(null, value, null);
end = head;
size++;
return true;
}else {
Node newNode = new Node(null, value, head);
head.pre = newNode;
head = newNode;
size++;
return true;
}
}
if (index == size){
return add(value);
}
Node mid = head;
if (index < size/2){
int tag = 1;
while (tag != index){
tag++;
mid = mid.next;
}
}else {
mid = end;
int tag = size;
while (tag != index){
tag--;
mid = mid.pre;
}
}
Node newNode = new Node(mid, value, mid.next);
mid.next = newNode;
newNode.next.pre = newNode;
size++;
return true;
}
public T remove(int index){
if (index < 0 || index >= size) throw new IllegalArgumentException("size =" + size + "; index = " + index);
if (index == 0){
T value = head.value;
if (size == 1){
head = null;
end = null;
}else {
head = head.next;
head.pre = null;
}
size--;
return value;
}
if (index == size-1){
T value = end.value;
end = end.pre;
end.next = null;
size--;
return value;
}
Node mid = head;
if (index < size/2){
int tag = 1;
while (tag != index){
tag++;
mid = mid.next;
}
}else {
mid = end;
int tag = size;
while (tag != index){
tag--;
mid = mid.pre;
}
}
Node removeNode = mid.next;
removeNode.next.pre = removeNode.pre;
removeNode.pre.next = removeNode.next;
size--;
return removeNode.value;
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
class Node{
T value;
Node pre;
Node next;
public Node(Node pre, T value, Node next) {
this.value = value;
this.pre = pre;
this.next = next;
}
}
}
// 服务器
// 1, 服务器是一个物理电脑
// 2, 有的时候指的是一个运行在物理服务器的一段程序
4, 栈的实现
先用链表: push, pop, peek
public class MyLinkedStack <T> {
private Node top;
private int size;
public boolean push(T value){
top = new Node(value, top);
size++;
return true;
}
public T pop(){
if (isEmpty())throw new RuntimeException("stack is empty");
T value = top.value;
top = top.next;
size--;
return value;
}
public T peek(){
if (isEmpty())throw new RuntimeException("stack is empty");
return top.value;
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
class Node{
T value;
Node next;
public Node(T value, Node next) {
this.value = value;
this.next = next;
}
}
}
用数组实现栈:
? 数组面临扩容问题
? 数组面临初始化的问题
public class MyArrayStack <T> {
private final int INIT_CAPACITY = 10;
private final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
private Object[] objs;
private int size ;
public MyArrayStack(){
this.objs = new Object[INIT_CAPACITY];
}
public MyArrayStack(int initCapacity){
if (initCapacity < 1 || initCapacity > MAX_CAPACITY) throw new IllegalArgumentException("parame is Illegal");
this.objs = new Object[initCapacity];
}
public boolean push(T value){
if (size == objs.length){
int newLen = getLen();
grow(newLen);
}
objs[size] = value;
size++;
return true;
}
private int getLen() {
int oldLen = objs.length;
int newLen = oldLen * 2;
if (newLen > MAX_CAPACITY || newLen < 0){
newLen = MAX_CAPACITY;
}
if (oldLen == newLen){
throw new RuntimeException(" stack can not add");
}
return newLen;
}
private void grow(int newLen) {
Object[] newObjs = new Object[newLen];
for (int i = 0; i < objs.length; i++) {
newObjs[i] = objs[i];
}
objs = newObjs;
}
public T pop(){
if (isEmpty()) throw new RuntimeException("stack is empty");
T oldValue = (T)objs[size - 1];
size--;
return oldValue;
}
public T peek(){
if (isEmpty()) throw new RuntimeException("stack is empty");
return (T)objs[size - 1];
}
public boolean isEmpty(){
return size == 0;
}
public int size(){
return size;
}
}
5, 队列实现
先用链表: 单链表
public class MyLinkedQueue <T> {
private Node head;
private Node end;
private int size;
public boolean offer(T value){
if (isEmpty()){
head = new Node(value, null);
end = head;
size++;
return true;
}
end.next = new Node(value, null);
end = end.next;
size++;
return true;
}
public T poll(){
if (isEmpty()) throw new RuntimeException("queue is empty");
if (size == 1){
T value = head.value;
head = null;
end = null;
size--;
return value;
}
T value = head.value;
head = head.next;
size--;
return value;
}
public T peek(){
if (isEmpty()) throw new RuntimeException("queue is empty");
return head.value;
}
public boolean isEmpty(){
return size == 0;
}
private int size(){
return size;
}
class Node {
T value;
Node next;
public Node(T value, Node next) {
this.value = value;
this.next = next;
}
}
}
用下数组:
注意一点:用链表实现队列时,里面的扩容方法是不能将数据直接一一对应的转移到新数组,因为有end和head指针,这样会使得后边一部分的元素为空:
所以扩容的代码应该这样写:
private void grow(int newLen) {
Object[] newObjs = new Object[newLen];
for (int i = 0; i < objs.length; i++) {
int tag = (head + i) % objs.length;
newObjs[i] = objs[tag];
}
objs = newObjs;
head = 0;
end = size - 1;
}
总体的写法是这样的:
public class MyArrayQueue <T> {
private final int INIT_CAPACITY = 10;
private final int MAX_CAPACITY = Integer.MAX_VALUE - 8;
private Object[] objs;
private int size ;
private int head;
private int end;
public MyArrayQueue(){
this.objs = new Object[INIT_CAPACITY];
}
public MyArrayQueue(int initCapacity){
if (initCapacity < 1 || initCapacity > MAX_CAPACITY) throw new IllegalArgumentException("parame is Illegal");
this.objs = new Object[initCapacity];
}
public boolean offer(T value){
if (size == objs.length){
int newLen = getLen();
grow(newLen);
}
if (isEmpty()){
objs[head] = value;
end = head;
size++;
return true;
}
end = (end + 1)%objs.length;
objs[end] = value;
size++;
return true;
}
private void grow(int newLen) {
Object[] newObjs = new Object[newLen];
for (int i = 0; i < objs.length; i++) {
int tag = (head + i) % objs.length;
newObjs[i] = objs[tag];
}
objs = newObjs;
head = 0;
end = size - 1;
}
private int getLen() {
int oldLen = objs.length;
int newLen = oldLen * 2;
if (newLen > MAX_CAPACITY || newLen < 0){
newLen = MAX_CAPACITY;
}
if (oldLen == newLen){
throw new RuntimeException(" stack can not add");
}
return newLen;
}
public T poll(){
if (isEmpty()) throw new RuntimeException("queue is empty");
if (size == 1){
T value = (T)objs[head];
head = 0;
end = 0;
size --;
return value;
}
T value = (T)objs[head];
head = (head + 1)% objs.length;
size--;
return value;
}
public T peek(){
if (isEmpty()) throw new RuntimeException("queue is empty");
return (T)objs[head];
}
public boolean isEmpty(){
return size == 0;
}
}
|