一文搞懂数据结构中的线性表
Posted TGB-Earnest
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文搞懂数据结构中的线性表相关的知识,希望对你有一定的参考价值。
思维导图
顺序存储结构
查找
给定一个n个元素有序的(升序)整型数组nums 和一个目标值target ,写一个函数搜索nums中的 target,如果目标值存在返回下标,否则返回 -1。
@Test
public void arrayTest()
int[] nums = new int[]-1,0,3,5,9,12;
int target = 9;
search(nums,target);
public int search(int[] nums, int target)
Boolean flag = false;
int count = -1;
if (nums.length== 0)
return count;
for (int i = 0; i < nums.length; i++)
if (nums[i]==target)
flag=true;
count=i;
if (flag == false)
count=-1;
return count;
我们看出这里面用到了一个for循环,时间复杂度为o(n),如果我们直接用nums[i] 获取元素,那么就是o(1)。
在顺序存储结构中元素是顺序存储的,所以知道了数组的下标会很快知道这个元素的。
移除
给你一个数组 nums和一个值 val,你需要 原地 移除所有数值等于val的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
int len = nums.length;
if (len==0)
return 0;
for (int i = 0; i < len; i++)
if (nums[i]==val)
for (int j = i ; j<nums.length-1;j++)
nums[j]=nums[j+1];
i--;
len--;
return len;
第一层for循环是寻找是否等于val的,注意这个时候一定要是len 而不能是num.length,因为len还要–。否则会造成死循环。
第二层for循环是为了让后面的元素覆盖前面的元素。
因为这个是顺序存储的,所以只能是覆盖。
链式存储结构
在链表类中实现这些功能:
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
//size存储链表元素的个数
int size;
//虚拟头结点
ListNode head;
//初始化链表
public MyLinkedList()
size = 0;
head = new ListNode(0);
/**
* get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
*/
public int get(int index)
//如果index 无效,返回-1
if (index<0 || index>=size)
return -1;
ListNode currentNode = head;
//查找值
for (int i = 0; i < size; i++)
currentNode=currentNode.next;
return currentNode.val;
/**
* 在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,
* 则该节点将附加到链表的末尾。
* 如果 index 大于链表长度,则不会插入节点。
* 如果index小于0,则在头部插入节点。
* @param index
* @param val
*/
public void addAtIndex(int index,int val)
//如果 index 大于链表长度,则不会插入节点。
if (index>size)
return;
//如果index小于0,则在头部插入节点。
if (index<0)
addAtIndex(0,val);// index 0
ListNode currentNode = head;
//找到插入节点的前驱
for (int i = 0; i < index; i++)
currentNode=currentNode.next;
//声明新的节点
ListNode newNode = new ListNode(val);
newNode.next=currentNode.next;
currentNode.next=newNode;
size++;
public void addAtHead(int value)
addAtIndex(0,value);
public void addAtTail(int value)
addAtIndex(size,value);
//如果索引index有效,则删除链表中的第index个节点
public void deleteAtIndex(int index)
if (index <0 || index>=size)
return;
ListNode currentNode =head;
//查找到index的前驱
for (int i = 0; i < index; i++)
currentNode= currentNode.next;
currentNode.next=currentNode.next.next;
size--;
这个设计链表就包含了查找某个链表,插入以及删除。我们可以看到链表的查找首先我们需要进行查找到这个索引index的位置,然后才能获取。插入和删除的时候,我们并不需要移动 其它的元素就可以了。
所以链表的查找的时间复杂度为O(n),插入和删除的时间复杂度为O(1)。造成这些根本的区别在于他们底层的存储 方式是不一样的,数组是顺序存储,而链表是牺牲了空间,多加了一个指针域。
温馨提示
我们可以在设计链表的时候引入了头结点的形式,也就是我们的虚拟结构。
若线性表为空表,则头结点的指针域为"空"
(1)头指针和头结点的区别:
头指针:
–头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针
–头指针具有标识作用,所以头指针冠以链表的名字(指针变量的名字)
–无论链表是否为空,头指针均不为空
–头指针是链表的必要元素
头结点:
–头结点是为了操作的统一和方便而设立的,放在第一个元素的结点之前,其数据域一般无意义(但也可以用来存放链表的长度)
–有了头结点,对在第一元素结点前插入结点和删除第一结点起操作与其它结点的操作就统一了
–头结点不一定是链表的必要元素
以上是关于一文搞懂数据结构中的线性表的主要内容,如果未能解决你的问题,请参考以下文章