数组 && 链表

数组

应用场景:
1)数据比较少
2)经常做的运算是按序号访问数据元素

面试题
选择题:
1)对于长度为n的线性表,建立其对应的单链表的时间复杂度为()。
O(1)
O(log2n)
O(n)
O(n^2)
2)下列哪些不是线性表?
队列

关联数组
链表
3)稀疏矩阵一般的压缩存储方法有两种,即()
二维数组和三维数组
三元组和散列
三元组和十字链表
散列和十字链表
4)将10阶对称矩阵压缩存储到一维数组A中,则数组A的长度最少为
100
40
55
80
5)
设A是n*n的对称矩阵,将A的对角线及对角线上方的元素以列为主的次序存放在一维数组B[1…n(n+1)/2]中,对上述任一元素aij (1≤i,j≤n,且i≤j)在B中的位置为()
i(i-1)/2+j
j(j-1)/2+i
j(j-1)/2+i-1
i(i-1)/2+j-1
6)若有定义:
int c[4][5],( *pc)[5];
pc=c;
那么,下列对数组C的元素引用正确的是( )。
pc+1

  • (pc+3)
  • (pc+1) +3
  • (*pc+2)

问答题:
1)数组和链表的区别
思路:
从逻辑结构上来看,数组必须实现定于固定的长度,不能适应数据动态增减的情况,即数组的大小一旦定义就不能改变。当数据增加是,可能超过原先定义的元素的个数;当数据减少时,造成内存浪费;链表动态进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。
从内存存储的角度看;数组从栈中分配空间(用new则在堆上创建),对程序员方便快速,但是自由度小;链表从堆中分配空间,自由度大但是申请管理比较麻烦。
从访问方式类看,数组在内存中是连续的存储,因此可以利用下标索引进行访问;链表是链式存储结构,在访问元素时候只能够通过线性方式由前到后顺序的访问,所以访问效率比数组要低。

2)输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)
思路:
Step1.从头到尾逐个累加数组中的每个数字,初始化和为0;(nCurrSum=0,nGreatestNum=int.MinValue)
Step2.首先加上第一个数字,从第二个数字开始累加,依次将累加和保存到一个临时变量(nCurrSum)中;
Step3.如果当前累加和(nCurrSum)小于0,那抛弃前面的子数组和,从下一个数字开始重新累加;相反,则将当前累加和(nCurrSum)与返回累加和(nGreatestNum)进行比较,如果nCurrSum>nGreatestNum,则更新nGreatestNum。

3)给定一个n个整型元素的数组a,其中有一个元素出现次数超过n / 2,求这个元素
思路:
方法一:对数组进行排序,因为一个元素已经超过了一半,则中间位置的元素就必为该元素。
方法二:火拼方法。

4)给定一个含有n个元素的数组,找出数组中的两个元素X和Y使得abs(x-y)最小
思路:
先排序,然后遍历元素。

5)求两个有序数组的共同元素
思路:
充分利用数组有序的性质,用两个指针i和j分别指向a和b,比较a[i]和b[j],根据比较结果移动指针,则有如下三种情况
a[i] < b[j],则i增加1,继续比较
a[i] == b[j],则i和j皆加1,继续比较
a[i] < b[j],则j加1,继续比较
重复以上过程直到i或j到达数组末尾。

6)给定两个有序整型数组a和b,各有n个元素,求两个数组中满足给定和的数对,即对a中元素i和b中元素j,满足i + j = d(d已知)
思路:
两个指针i和j分别指向数组的首尾,然后从两端同时向中间遍历。

7)给定含有n个元素的整型数组a,其中包括0元素和非0元素,对数组进行排序,要求:
排序后所有0元素在前,所有非零元素在后,且非零元素排序前后相对位置不变,且不能使用额外存储空间
思路:
从尾部开始设置两个指针,last指针指向后面第一个出现0的位置,first指向last前面的第一个非0元素,如果first是非0,则将值赋给last,然后last++,first++;如果first指向0,则继续前进first++。

8)给定一个有序整数序列(非递减序),可能包含负数,找出其中绝对值最小的元素,比如给定序列 -5, -3, -1, 2, 8 则返回1
思路:
如果给定的序列中所有的数都是正数,那么数组的第一个元素即是结果。
如果给定的序列中所有的数都是负数,那么数组的最后一个元素即是结果。
如果给定的序列中既有正数又有负数,那么绝对值得最小值一定出现在正数和负数的连接处。

9)未排序数组,找出两个元素值等于target值,并返回下标,小的下标在前面。
思路:
定义一个空字典,values存储nums数组索引,keys存储nums数据的值。循环整个数组,判定target-num[i]是否在字典在,在执行相应操作

链表

使用场景:
1)数据量较小
2)不需要预先知道数据规模
3)适应于频繁的插入操作

面试题
选择题:
1)在一个单链表中,q的前一个节点为p,删除q所指向节点,则执行()。
delete q
q->next=p->next;delete p
p->next=q->next;delete p
p->next=q->next;delete q
delete p
q->next=p->next;delete q
2)从表中任意一个节点出发可以依次访问到表中其他所有节点的结构是()
线性单链表
双向链表
循环链表
线性链表
3)若某表最常用的操作是在最后一个结点之后插入一个结点或删除最后一个结点,则采用 存储方式最节省运算时间。
单链表
给出表头指针的单循环链表
双链表
带头结点的双循环链表
4)和顺序栈相比,链栈有一个比较明显的优势是()
通常不会出现栈满的情况
通常不会出现栈空的情况
插入操作更容易实现
删除操作更容易实现
5)某指针变量p指向双向链表中结点A,指针变量s指向被插入的结点X,则在结点A的后面插入结点X的操作序列为( )。
p->right=s;s->left=p;p->right->left=s;s->right=p->right;
s->left=p;s->right=p->right;p->right=s;p->right->left=s;
p->right=s;p->right->left=s;s->left=p;s->right=p->right;
s->left=p;s->right=p->right;p->right->left=s; p->right=s;
6)
某单链表有5个元素,设单链表的节点结构为(data,next),5个元素的data依次为(1、2、3、4、5),已知指针q指向节点3,指针p指向节点4,那么下面操作能将链表变为data依次为(1、2、3、5)的是____。(其中temp为节点类型指针,默认指向NULL)
q=p->next;
p=q->next;
p->next=q->next;
q->next=p->next; delete q;
p->data=p->next->data; p->next=p->next->next; delete p->next;
temp = p->next; p->next=temp->next; p->data=temp->data; delete temp;temp=NULL;

问答题:
1)寻找单链表的中间元素
思路:
设置 两个指针 slow、fast 起始都指向单链表的头节点。其中 fast 的移动速度是 slow 的2倍。当 fast 指向末尾节点的时候,slow 正好就在中间了。想想一下是不是这样假设一个链表长度为 6 , slow 每次一个节点位置, fast 每次移动两个节点位置,那么当fast = 5的时候 slow = 2 正好移动到 2 的节点的位置。

2)已知一个单链表求倒数第 N 个节点
思路:
快慢指针法,让快指针先走 n-1 步后,然后让慢指针出发。快慢指针每次都只移动一个位置,当快指针移动到链表末尾的时候,慢指针就正处于倒数第 N 个节点的位置。

3)删除单链表的倒数第 n 个节点
思路:
想操作链表的某个节点(添加,删除)还必须知道这个节点的前一个节点。所以我们删除倒数第 n 个元素就要找到倒数第 n + 1 个元素。然后将倒数第 n + 1个元素 p 的 next 指针 p.next 指向 p.next.next 。

4)翻转一个单链表,要求额外的空间复杂度为 O(1)
思路:
找到当前要反转的节点的下一个节点并用变量保存因为下一次要反转的是它
然后让当前节点的 next 指向上一个节点, 上一个节点初始 null 因为头结点的翻转后变为尾节点
当前要反转的节点变成了下一个要比较元素的上一个节点,用变量保存
当前要比较的节点赋值为之前保存的未翻转前的下一个节点
当前反转的节点为 null 的时候,保存的上一个节点即翻转后的链表头结点

5)单链表的归并排序
代码:

private Node merge(Node l, Node r) {

   //创建临时空间
   Node aux = new Node();
   Node cur = aux;

   //由于链表不能方便的拿到链表长度 所以一般使用 while l == null 表示链表遍历到尾部
   while (l != null && r != null) {
       if (l.value < r.value) {
           cur.next = l;
           cur = cur.next;
           l = l.next;
       } else {
           cur.next = r;
           cur = cur.next;
           r = r.next;
       }
   }
   //当有一半链表遍历完成后 另外一个链表一定只剩下最后一个元素(链表为基数)
   if (l != null) {
       cur.next = l;
   } else if (r != null) {
       cur.next = r;
   }

   return aux.next;
}

6)单链表的插入排序
代码:

public Node insertionSortList(Node head) {
        if (head == null || head.next == null) return head;

        Node dummyHead = new Node(0);
        Node p = head;
        dummyHead.next = head;
      //p 的值不小于下一节点元素考察下一节点
        while (p.next != null) {
            if (p.value <= p.next.value) { 
                p = p.next;
            } else {
                //p 指向 4
                Node temp = p.next;
                Node q = dummyHead;
                p.next = p.next.next;

                //从头遍历链表找到比当前 temp 值小的第一个元素插入其后边 整个位置一定在 头节点与 q 节点之间
                while (q.next.value < temp.value && q.next != q)
                    q = q.next;

                temp.next = q.next;
                //重新连接链表 注意 else 的过程并没有改变 p 指针的位置
                q.next = temp;
            }
        }
        return dummyHead.next;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值