线性表

线性表的基本概念

1.对于同一个线性表,其每一个数据元素的值虽然不同,但必须具有相同的数据类型;
2.数据元素之间具有一种线性的或“一对一”的逻辑关系。
3. 第一个数据元素没有前驱,这个数据元素被称为开始节点;
4. 最后一个数据元素没有后继,这个数据元素被称为终端节点;
5. 除了第一个和最后一个数据元素外,其他数据元素有且仅有一个

线性表L定义为:
在这里插入图片描述

  • 线性表由n个元素构成
  • 当n = 0时, ( ) 表示空线性表
  • 当n >1时,表中第一个元素有唯一的后继,最后一个元素有唯一的前驱,其余元素有唯一的后继和前驱,因而呈现线性关系

线性表的操作主要包括

  • 计算表的长度n
  • 判断表是否为空
  • 判断表中是否包含某个元素o
  • 在表中查找某个元素o
  • 访问第i个元素,0≤i < n
  • 将新值赋予第i个元素,0≤i < n
  • 将新元素插入第i个位置,0≤i < n
  • 删除第i个元素,0≤i < n
    线性表ADT(Abstract Data Type)
    抽象数据类型ADT:一个数据结构及定义在该结构上的一组操作的总称
    在这里插入图片描述
    ##数组
    是一种基本的数据结构,用于按顺序存储元素的集合。但是元素可以随机存取,因为数组中的每个元素都可以通过数组索引来识别。

数组可以有一个或多个维度。这里我们从一维数组开始,它也被称为线性数组。这里有一个例子:

在上面的例子中,数组 A 中有 6 个元素。也就是说,A 的长度是 6 。我们可以使用 A[0] 来表示数组中的第一个元素。因此,A[0] = 6 。类似地,A[1] = 3,A[2] = 8,依此类推。

数组中的操作

// "static void main" must be defined in a public class.
public class Main {
    public static void main(String[] args) {
        // 1. Initialize
        int[] a0 = new int[5];
        int[] a1 = {1, 2, 3};
        // 2. Get Length
        System.out.println("The size of a1 is: " + a1.length);
        // 3. Access Element
        System.out.println("The first element is: " + a1[0]);
        // 4. Iterate all Elements
        System.out.print("[Version 1] The contents of a1 are:");
        for (int i = 0; i < a1.length; ++i) {
            System.out.print(" " + a1[i]);
        }
        System.out.println();
        System.out.print("[Version 2] The contents of a1 are:");
        for (int item: a1) {
            System.out.print(" " + item);
        }
        System.out.println();
        // 5. Modify Element
        a1[0] = 4;
        // 6. Sort
        Arrays.sort(a1);
    }
}
  • 每种编程语言中,基本都会有数组这种数据类型

用一组连续的内存空间,来存储一组具有相同类型的数据

  • 支持“随机访问”
    在这里插入图片描述
int[] arr = new int[100];
arr[99] = 1;
int arr2[] = {10, 20, 30, 40, 50};
for (int i = 0; i < arr.length; i++)
   System.out.println("Element at index " + i + " : "+ arr[i]);

动态数组

正如我们在上一篇文章中提到的,数组具有固定的容量,我们需要在初始化时指定数组的大小。有时它会非常不方便并可能造成浪费。

因此,大多数编程语言都提供内置的动态数组,它仍然是一个随机存取的列表数据结构,但大小是可变的。例如,在 C++ 中的 vector,以及在 Java 中的 ArrayList。
动态数组中的操作

 // "static void main" must be defined in a public class.
public class Main {
    public static void main(String[] args) {
        // 1. initialize
        List<Integer> v0 = new ArrayList<>();
        List<Integer> v1;                           // v1 == null
        // 2. cast an array to a vector
        Integer[] a = {0, 1, 2, 3, 4};
        v1 = new ArrayList<>(Arrays.asList(a));
        // 3. make a copy
        List<Integer> v2 = v1;                      // another reference to v1
        List<Integer> v3 = new ArrayList<>(v1);     // make an actual copy of v1
        // 3. get length
        System.out.println("The size of v1 is: " + v1.size());;
        // 4. access element
        System.out.println("The first element in v1 is: " + v1.get(0));
        // 5. iterate the vector
        System.out.print("[Version 1] The contents of v1 are:");
        for (int i = 0; i < v1.size(); ++i) {
            System.out.print(" " + v1.get(i));
        }
        System.out.println();
        System.out.print("[Version 2] The contents of v1 are:");
        for (int item : v1) {
            System.out.print(" " + item);
        }
        System.out.println();
        // 6. modify element
        v2.set(0, 5);       // modify v2 will actually modify v1
        System.out.println("The first element in v1 is: " + v1.get(0));
        v3.set(0, -1);
        System.out.println("The first element in v1 is: " + v1.get(0));
        // 7. sort
        Collections.sort(v1);
        // 8. add new element at the end of the vector
        v1.add(-1);
        v1.add(1, 6);
        // 9. delete the last element
        v1.remove(v1.size() - 1);
    }
}
     
  • 重新开辟一块大小为当前容量两倍的数组
  • 把原数据拷贝过去
  • 释放掉旧的数组
    在这里插入图片描述动态数组 – 插入元素
    在这里插入图片描述动态数组 – 删除元素
    在这里插入图片描述

二维数组

类似于一维数组,二维数组也是由元素的序列组成。但是这些元素可以排列在矩形网格中而不是直线上。

Leetcode数组练习
283. Move Zeroes

思路: 设置一个index,表示非0数的个数,循环遍历数组,
如果不是0,将非0值移动到第index位置,然后index + 1
遍历结束之后,index值表示为非0的个数,再次遍历,从index位置后的位置此时都应该为0

public class RemoveZeroes {
    public static void main(String[] args) {
       int[]arr={1,2,0,0,5};
       moveZeroes(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]+" ");
        }
    }
    public static int[] moveZeroes(int[] nums) {
        int index=0;
        for (int i = 0; i < nums.length; i++) {
          if (nums[i]!=0){
              nums[index]=nums[i];
              index++;

          }
        }
        for (int i = index; i < nums.length; i++) {
           nums[i]=0;
        }
        return nums;
    }
}
  1. Remove Element
    思路 用j表示不等于val的个数
public int removeElement(int[] nums, int val) {
		if (nums == null || nums.length == 0)
			return 0;
		int j = 0;
		for (int i = 0; i < nums.length; i++) {
			if (nums[i] != val) {
				nums[j] = nums[i];
				j++;
			}
		}
		return j;
	}
  1. Plus One
class Solution {
    public int[] plusOne(int[] digits) {
        for (int i = digits.length - 1; i >= 0; i--) {
			if (digits[i] != 9) {
                                digits[i]++;
				return digits;
			} 
			digits[i] = 0;
		}
                //跳出for循环,说明数字全部是9
		int[] temp = new int[digits.length + 1];
		temp[0] = 1;
		return temp;
    }
}

26 Remove Duplicates from Sorted Array

if (nums.size() < 2) return nums.size();
	int j = 0;
	for (int i = 1; i < nums.size(); i++)
		if (nums[j] != nums[i]) nums[++j] = nums[i];
	return ++j;

80 Remove Duplicates from Sorted Array II

思路;

index=2 从i=2开始,如果nums[index-2]!=num[i] nums[index]=num[i] idex++
return index; ```java

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length <= 2) return nums.length;
       
 int index = 2;
    for(int i = 2; i < nums.length; i++){
        if(nums[i] != nums[index-2])
            nums[index++] = nums[i];
    }
    
    return index;
}
}

167 Two Sum II - Input array is sorted

> class Solution {
>     public int[] twoSum(int[] numbers, int target) {
>      int[] res = new int[2];
>         int i = 0, j = numbers.length - 1;
>         while (i < j) {
>             if (numbers[i] + numbers[j] == target) break;
>             if (numbers[i] + numbers[j] > target) j--;
>             if (numbers[i] + numbers[j] < target) i++;
>         }
>         res[0] = i + 1;
>         res[1] = j + 1;
>         return res;
>     }
> 
> }
  1. Rotate Array
class Solution {
    /**
     * 双重循环
     * 时间复杂度:O(kn)
     * 空间复杂度:O(1)
     */
    public void rotate_1(int[] nums, int k) {
        int n = nums.length;
        k %= n;
        for (int i = 0; i < k; i++) {
            int temp = nums[n - 1];
            for (int j = n - 1; j > 0; j--) {
                nums[j] = nums[j - 1];
            }
            nums[0] = temp;
        }
    }
    }
  1. Third Maximum Number
class Solution {
    public int thirdMax(int[] nums) {
        if(nums.length==1){
            return nums[0];
        }
        if(nums.length==2){
            return Math.max(nums[0],nums[1]);
        }
        int max1=Integer.MIN_VALUE;
        int max2=Integer.MIN_VALUE;
        int max3=Integer.MIN_VALUE;
        boolean f=true;
        int flag=0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==Integer.MIN_VALUE&&f){
                flag++;
                f=false;
            }
            if(nums[i]>max1){
                flag++;
                //原先第二大传递给第三大
                max3=max2;
                //原先最大值传递给第二大
                max2=max1;
                //更新最大值
                max1=nums[i];
            }else if (nums[i]>max2 && nums[i]<max1){
                flag++;
                max3=max2;
                max2=nums[i];
            } else if( nums[i]>max3 && nums[i]<max2){
                flag++;
                max3=nums[i];
            }
        }
        return flag>=3?max3:max1;
    }
}

. Valid Mountain Array

class Solution {
    public boolean validMountainArray(int[] A) {
        if(A==null ||A.length==0){
            return false;
        }
        int i=0;
        //上山
        while(i<A.length-1 && A[i]<A[i+1]){
            i++;
        }
        //此时的i为山脉顶峰
        if(i==0 || i==A.length-1){
            return false;
        }
        //下山
        while(i<A.length-1 && A[i]>A[i+1]){
            i++;
        }
        if(i!=A.length-1){
            return false;
        }
        return true;
    }
}
  1. Monotonic Array
class Solution {
    public boolean isMonotonic(int[] A) {
 int upNum = 0;//记录数组递增次数
        int downNum = 0;//记录数组递减次数
        for (int i = 0; i < A.length-1; i++) {
            if (A[i] < A[i + 1]) {
                //前一个小于后一个,说明该数组单调递增
                if (downNum != 0){
                    //如果发现之前存在单调递减的情况,则直接返回false
                    return false;
                }else{
                    upNum++;//递增次数加一
                }
            }
            if (A[i] > A[i + 1]) {
                //前一个大于后一个,说明该数组单调递减
                if (upNum != 0) {
                    //如果发现之前存在单调递增的情况,则直接返回false
                    return false;
                }else{
                    downNum++;//递减次数加一
                }
            }
        }
        return true;
        }
        }

链表

链表是一种物理上地址非连续、非顺序的存储结构
数据元素的逻辑顺序是通过链表中的指针链接次序实现
链表由一系列结点组成
每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域

单链表

单链表中的每个结点不仅包含值,还包含链接到下一个结点的引用字段。通过这种方式,单链表将所有结点按顺序组织起来。、

下面是一个单链表的例子:

在这里插入图片描述蓝色箭头显示单个链接列表中的结点是如何组合在一起的。
以下是单链表中结点的典型定义:

public class SinglyListNode {
    int val;
    SinglyListNode next;
    SinglyListNode(int x) { val = x; }
}

在大多数情况下,我们将使用头结点(第一个结点)来表示整个列表。
操作
与数组不同,我们无法在常量时间内访问单链表中的随机元素。 如果我们想要获得第 i 个元素,我们必须从头结点逐个遍历。 我们按索引来访问元素平均要花费 O(N) 时间,其中 N 是链表的长度。

例如,在上面的示例中,头结点是 23。访问第 3 个结点的唯一方法是使用头结点中的“next”字段到达第 2 个结点(结点 6); 然后使用结点 6 的“next”字段,我们能够访问第 3 个结点。

你可能想知道为什么链表很有用,尽管它在通过索引访问数据时(与数组相比)具有如此糟糕的性能。
添加操作 - 单链表
如果我们想在给定的结点 prev 之后添加新值,我们应该:

1、 使用给定值初始化新结点 cur;
在这里插入图片描述2、将 cur 的“next”字段链接到 prev 的下一个结点 next;
在这里插入图片描述3、将 prev 中的“next”字段链接到 cur 。
在这里插入图片描述与数组不同,我们不需要将所有元素移动到插入元素之后。因此,您可以在 O(1) 时间复杂度中将新结点插入到链表中,这非常高效。
在开头添加结点
众所周知,我们使用头结点来代表整个列表。

因此,在列表开头添加新节点时更新头结点 head 至关重要。

  1. 初始化一个新结点 cur;

  2. 将新结点链接到我们的原始头结点 head。

  3. 将 cur 指定为 head。
    删除操作 - 单链表
    如果我们想从单链表中删除现有结点 cur,可以分两步完成:

    1找到 cur 的上一个结点 prev 及其下一个结点 next;
    在这里插入图片描述2接下来链接 prev 到 cur 的下一个节点 next。
    在这里插入图片描述在我们的第一步中,我们需要找出 prev 和 next。使用 cur 的参考字段很容易找出 next,但是,我们必须从头结点遍历链表,以找出 prev,它的平均时间是 O(N),其中 N 是链表的长度。因此,删除结点的时间复杂度将是 O(N)。

空间复杂度为 O(1),因为我们只需要常量空间来存储指针。

双向链表

双向循环链表是单向循环链表的每个结点中,再设置一个指向其前驱结点的指针域
好处:可以进行两个方向的查找,但是插入和删除时比较麻烦

在这里插入图片描述操作
与单链表类似,我们将介绍在双链表中如何访问数据、插入新结点或删除现有结点。

我们可以与单链表相同的方式访问数据:

我们不能在常量级的时间内访问随机位置。
我们必须从头部遍历才能得到我们想要的第一个结点。
在最坏的情况下,时间复杂度将是 O(N),其中 N 是链表的长度

小结 - 链表
让我们简要回顾一下单链表和双链表的表现。

它们在许多操作中是相似的。

它们都无法在常量时间内随机访问数据。
它们都能够在 O(1) 时间内在给定结点之后或列表开头添加一个新结点。
它们都能够在 O(1) 时间内删除第一个结点。

但是删除给定结点(包括最后一个结点)时略有不同。

在单链表中,它无法获取给定结点的前一个结点,因此在删除给定结点之前我们必须花费 O(N) 时间来找出前一结点。
在双链表中,这会更容易,因为我们可以使用“prev”引用字段获取前一个结点。因此我们可以在 O(1) 时间内删除给定结点。

Leetcode链表练习

707 Design Linked List
206 Reverse Linked List
237 Delete Node in a Linked List
83 Remove Duplicates from Sorted List
80 Remove Duplicates from Sorted Array II
203 Remove Linked List Elements
328 Odd Even Linked List
234 Palindrome Linked List
160 Intersection of Two Linked Lists
876 Middle of the Linked List
19 Remove Nth Node From End of List

如果你需要经常添加或删除结点,链表可能是一个不错的选择。
如果你需要经常按索引访问元素,数组可能是比链表更好的选择

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值