数据结构之数组和链表

数组

1 是顺序存储的相同数据类型的数据结构,在内存空间上地址也是连续的,所以在声明数组的时候就要确定长度

声明数组

静态创建数组 
int array[100];//定义一个大小100的数组,没有初始化
int array[2]= {1,2};//定义长度为2的数组,并初始化
int array[20]= {1,2};//定义长度为20的数组,并初始化前两个
new动态创建:使用new分配一个数组,返回指向第一个元素的指针
int* array = new int[100]; delete []array;//分配长度为100的数组并删除
int* array = new int[100]1,2; delete []array;//分配长度为100的数组并初始化前两个元素,再删除
也可用malloc:
int a[100] = malloc(sizeof(int) * 100);
字符串数组  char b[] = “abd”;//定义大小为4的字符数组,因为字符串结尾以‘\0’结束
字符数组  char b[] = {'a','b'};//定义大小为2的字符数组

2 数组可以通过下标访问元素,速度很快,但是同样插入或者删除元素就很困难,需要移动大量的元素

3 数组存在下标越界问题,下标的范围应该大于等于0,小于数组的大小

链表

1 物理存储单元上不连续,非顺序的存储结构

2 数据的逻辑顺序是通过指针链接次序实现的,每个节点包括数据域和指针域。空间开销大

3 长度不固定,可以任意的增加删除元素,但是不能做到随机访问

数组缺点需要预先知道数据大小,链表缺点不能像数组一样随机读取,增加结点指针域,空间开销大,优点方便插入跟删除任意位置结点

数组和链表有以下几点不同:

  1. 存储形式:数组是一块连续的空间,声明时就要确定长度。链表是一块可不连续的动态空间,长度可变,每个结点要保存相邻结点指针。
  2. 数据查找: 数组的线性查找速度快, 查找操作直接使用偏移地址。 链表需要按顺序检索结点,效率低。
  3. 数据插入或删除。链表可以快速插入和删除结点, 而数组则可能需要大量数据移动
  4. 越界问题: 链表不存在越界问题,数组有越界问题。

说明:一定要根据实际需求进行选择。数组便于查询(定位访问),链表便于插入删除。数组节省空间但是长度固定,链表虽然变长但是占了更多的存储空间。

struct ListNode
{
	int val;//数据域
	ListNode *next;//指针域,指向下一个节点的指针
	ListNode(int x) : val(x), next(NULL) {}//初始化当前节点值为x, 指针为空(这一行是构造函数)
};

数组和链表的应用场景(待补充,又看到这篇文章的可以一起补充啊,互相交流)

单链表
//头插法:每个新节点都插入到头结点后面,第一个插进去的是尾节点,所以打印出来是倒序
newNode->data = x;
newNode->next = headNode->next;
headNode->next = newNode;
//尾插法:每个新节点都插入到这个链表的后面,所以多一个始终指向表尾的指针
ListNode *rear;
rear = headNode;//一开始头结点是表尾
newNode->data = x;
rear->next = newNode;
rear = newNOde;//rear始终指向表尾
所有节点插入后,循环之后,rear->next = NULL;
插入节点

链表不要断,先让新节点的next指向要插入的那个节点,然后让那个节点的前驱节点的next指向新节点

p->next = cur->next;//p是新节点,cur是要插入位置的前驱节点
cur->next = p;
双链表

每个节点都有两个指针,分别指向直接前驱和直接后继,相比单链表更方便找到直接前驱节点和后继节点

循环链表(待补充)
单链表逆置,反转链表

循环算法,迭代,思路就是就地箭头转过来完整代码参见单链表逆序

ListNode* reverseList(ListNode* head) {
        if (head == NULL) return head;//注意一定要考虑指针是否为空,不然就是思路不周祥
        ListNode* pre = NULL;
        ListNode* cur = head;
        while (cur) {
            ListNode* p = cur->next;//cur跟p依次后移
            cur->next = pre;//箭头转过来,类似交换两数的
            pre = cur;
            cur = p;
        }
	        return pre;}

递归

ListNode* reverseList(ListNode* head) {
        if (head == NULL || head->next == NULL) return head;
        ListNode* p = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return p;
链表合并
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL) return l2;
        if(l2 == NULL) return l1;
        ListNode *dummy = new ListNode(0);
        ListNode *head = dummy;
        
        while(l1 && l2){
            if(l1 ->val < l2 ->val){//升序,那个数字小,就指向哪个
                head->next = l1;
                l1 = l1->next;
            }
            else{
                head ->next = l2;
                l2 =l2->next;
            }
            head = head->next;
        }
        head ->next = l1 ? l1 : l2;
        return dummy->next;}
判断一个链表是否有环博客判断链表是否有环
链表有环找到环的起始点(待补充)

list数据结构
list是由双向链表实现的,因此内存空间是不连续的。只能通过指针访问,所以list的随机存取效率低,时间复杂度为o(n);
但由于链表的特点,能高效地进行插入和删除。

手写代码:(待补充)
1、一个单向链表,每个节点上都有一个value;给定 一个 value,将该 value 之前的所有 value返回

7、手写代码:将一个数组指定offset后面的元素前置,可采用链表实现

8、手写代码:循环链表插入元素

你可能感兴趣的:(数据结构和算法,数据结构,数组,链表,区别,单链表)