Java数据结构——顺序表和单链表

前言:
学习算法,不论对思考问题的方式还是对编程的思维都会有很大的好处。
而数据结构在算法的这个概念里是非常重要的,学好了数据结构就能写出更好的代码,所以必须花费精力和时间去好好学习。
要想学好数据结构就必须先掌握好基础。本节对数据结构里的两种基本的线性表(顺序表和单链表)的基本内容进行了详细的描述,理解并且熟练运用这些很重要。

正文:

首先来说什么是时间复杂度和空间复杂度?
复杂度:粗鲁衡量算法好坏的刻度尺(工具)
两个维度:快慢 时间复杂度(重点)
使用空间的情况 空间复杂度

时间复杂度:
直接利用允许时间衡量不现实。测试环境多变,不好控制变量
前提:如果指定CPU的情况下,单位时间内运行的基本指令个数是固定的
如果一个算法需要的指令比另一个算法需要的指令个数小,就可以推出县一个算法的时间复杂度更小
前提:算法计算的快慢和输入数据的规模是有关系的

粗略计算算法的快慢
n:数据规模
f(n):n的数据规模情况下,需要的大概基本指令个数
引入大O渐进表示法
O(f(n))
1)只保留最高次项
2)保留的最高次项系数化为1
算法的快慢分为最好情况、平均情况、最坏情况
一般优先关注最坏情况,其次是平均情况,最后是最好情况。

空间复杂度:
O(F(n)) 在n输入规模的情况下,算法需要的最大的空间情况(额外的)
1)开辟数组
2)调用栈(递归方法)

1.顺序表
用Java实现顺序表的 增 、删、查、改

public class MyArrayList {
	private int[] array;		// 代表的是存在数据的数组
								
	private int size;			// 记录顺序表的已有数据个数
	
	public MyArrayList() {
		// 1. 申请空间
		array = new int[2];
		// 2. 初始化数据个数
		size = 0;
	}

扩容:扩容的空间越小,空间的浪费越小
扩容的空间越大,需要扩容的频率越少
经验值 大概是1.5倍或2倍

// 扩容
	public void ensureCapacity(){
		if(size<array.length){
			return;
		}
		int newCapacity=array.length*2;
		int[] newArray= new int[newCapacity];
		for(int i=0;i<size;i++){
			newArray[i]=array[i];
		}
		array=newArray;
	}

尾插

//尾插
	public void pushBack(int element){
		ensureCapacity();
		array[size++]=element;
	}

头插

//头插
	public void pushFront(int element){
		ensureCapacity();
		for(int i=size-1;i>=0;i--){
			array[i+1]=array[i];
		}
		array[0]=element;
		size++;
	}

按下标插入指定位置

//插入指定位置
	public void insert(int index,int element){
	
		if(index < 0 || index > size){
			System.err.println("插入位置错误");
			return;
		}
		ensureCapacity();
		for (int i = size - 1; i >= index; i--) {
			array[i + 1] = array[i];
		}
		array[index] = element;
		size++;
	}

尾删

//尾删
	public void popBack(){
		if(size<=0){
			System.out.println("顺序表为空");
			return;
		}
		array[--size]=0;
	}

头删

//头删
	public void popFront(){
		if(size<=0){
			System.out.println("顺序表为空");
			return;
		}
		for(int i=1;i<size;i++){
			array[i-1]=array[i];
		}
		array[--size]=0;
	}

按下标删除指定位置元素

	//删除指定位置元素
public void earse(int index) {
		if (size <= 0) {
			System.err.println("顺序表为空");
			return;
		}
		
		if (index < 0 || index >= size) {
			System.err.println("下标错误");
			return;
		}
		
		for (int i = index + 1; i < size; i++) {
			array[i - 1] = array[i];
		}
		
		array[--size] = 0;
	}

按元素查找

//查找
	public int indexOf(int element){
		for(int i=0;i<size;i++){
		if(array[i]==element){
			return i;
		}
		}
		return -1;
	}

按下标查找

public int get(int index){
		if(index<0 || index>=size){
			System.err.println("下标错误");
			return -1;
		}
	      return array[index];
	}

只删除第一个出现的元素

//删除掉某一元素,如果多次出现,删除第一次出现的
	public void remove(int element){
		int index=indexOf(element);
		if(index!=-1){
			earse(index);
		}
		else{
			System.out.println("元素不存在");
	}

全部删除
下面三种方法都能实现,只是按复杂度越来越优

多想几种方法,防止思维定势,选取较好的来编程

public void removeAll(int element){
		int index;
		while((index=indexOf(element))!=-1){
			earse(index);
		}
	}
public void removeAll(int element){
		int j=0;
		int[] newArray = new int[array.length];
		for(int i=0;i<size;i++){
			if(array[i]!=element){
				newArray[j++]=array[i];
			}
		}
	}
public void removeAll(int element){
		int j=0;
		for(int i=0;i<size;i++){
			if(array[i]!=element){
			array[j++]=array[i];
		}
		size=j;
	}
	}

修改

//修改
	public void set(int index,int element){
		if(index<0 || index>=size){
			System.err.println("下标错误");
			return ;
		}
		array[index]=element;
	}

打印

//打印
	public void print() {
		System.out.println("打印顺序表: 当前容量: " + array.length);
		for (int i = 0; i < size; i++) {
			System.out.print(array[i] + " ");
		}
		System.out.println();
	}

下面两个程序很短也写在方法里,很好的体现了Java的封装性

public int size(){
		return size;
	}
public boolean isEmpty(){
		return size==0;
	}

最后进行测试,查看结果与预期的结果是否一致

public static void main(String[] args){
		MyArrayList list= new MyArrayList();
		list.print();
		list.pushBack(1);
		list.pushBack(2);
		list.pushBack(3);
		list.print();	// 1 2 3
		list.pushFront(10);
		list.pushFront(20);
		list.pushFront(30);
		list.print();	// 30 20 10 1 2 3
		list.insert(3, 100);
		list.print();	// 30 20 10 100 1 2 3
		list.insert(20, 200);	// 报错
		
		list.earse(2);
		list.earse(2);
		list.print();	// 30 20 1 2 3
		list.popFront();
		list.popFront();
		list.popFront();
		list.print();	// 2 3
		list.popBack();
		list.popBack();
		list.print();	// 空的
		list.popBack();	// 报错
	}
}

2.单链表
单链表的增和删

class Node {
	int val;	
	Node next;	

	Node(int val) {
		this.val = val;
		this.next = null;
	}
	
	public String toString() {
		return String.format("Node(%d)", val);
	}
}
public class MyLinkedList {

1.头插
head: 原来的第一个结点
val:要插入的值
返回:新的第一个结点

private static Node pushFront(Node head, int val) {
		// 1. 结点
		Node node = new Node(val);
		// 2. 让原来的 head 成为 node 的下一个结点
		node.next = head;
		// 3. 更新第一个结点的引用
		return node;
	}

2.尾插
尾插需要分情况讨论:
1.空链表的情况
就是让新的结点成为第一个结点
2.非空链表
如果要插入的数据没有结点,先给他装入一个结点中
Node node = new Node(val);
让新结点的next=null;
找到倒数第一个结点(子问题),找到.next=null的结点
Node last=head;
while(last.next!=null){last=last.next}
last就是最后一个结点
让原来的倒数第一个结点的下一个为新结点
last.next=node;

private static Node pushBack(Node head, int val) {
		Node node = new Node(val);
		if (head == null) {
			return node;
		} else {
			Node last = head;
			while (last.next != null) {
				last = last.next;
			}
			last.next = node;
			
			return head;
		}
	}

3.头删
head=head.next; 原来的第一个结点会因为没有指向被回收

private static Node popFront(Node head) {
		if (head == null) {
			System.err.println("空链表无法删除");
			return null;
		}
		
		// 原来第一个结点,会因为没有引用指向而被回收
		return head.next;
	}

4.尾删
尾删分情况:
1.只有一个结点
head.nextnull;
return null;
2.有两个以上的结点
1.找到倒数第二个结点
Node lastSecond=head;
LastSecond.next.next
null
LastSecond=lastSecond.next;
2.让倒数第二个结点的.next为空

private static Node popBack(Node head) {
		if (head == null) {
			System.err.println("空链表无法删除");
			return null;
		}
		
		if (head.next == null) {
			return null;
		} else {
			Node lastSecond = head;
			while (lastSecond.next.next != null) {
				lastSecond = lastSecond.next;
			}
			
			lastSecond.next = null;
			return head;
		}
	}

5.打印
这里的print(cur)等于print(cur.toString)

// 打印
	private static void print(Node head) {
		System.out.println("打印链表:");
		for (Node cur = head; cur != null; cur = cur.next) {
			System.out.print(cur + " --> ");
		}
		System.out.println("null");
	}

最后进行测试

public static void main(String[] args){
			Node head=null;
			head=pushFront(head,1);
			head=pushFront(head,2);
			head=pushFront(head,3);
			print(head);      //3,2,1
			head=pushBack(head,5);
			head=pushBack(head,6);
			head=pushBack(head,7);
			print(head);    //  3,2,1,5,6,7
			head=popFront(head);//2,1,5,6,7
			print(head); 
			head=popBack(head);  //2,1,5,6
			print(head); 
		}

要学会画图来理解单链表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值