RecyclerView中ViewHolder重用机制理解,解决网络图片错乱和闪烁问题

article/2025/9/28 18:50:58

🖥 原文转载自

  • https://blog.csdn.net/xyq046463/article/details/51800095
  • 作者:lanceJin. https://www.jianshu.com/p/ef7d0e16d999
  • 部分来自作者内容补充

文章目录

  • 1. 概述
  • 2. 验证item的变化过程
  • 3. 异步网络导致ViewHolder出现图片错乱的原因
    • 3.1 场景分析
    • 3.2 解决方法
    • 3.3 解决方式一
    • 3.4 解决方式二
    • 3.5 注意内存泄漏的风险
  • 4. RecyclerView网络图像刷新会闪烁
    • 4.1 全局刷新
    • 4.2 局部刷新

1. 概述

对于使用ViewHolder引起的图片错乱问题,相信大部分人都有遇到过,我也一样,对于解决方法也有所了解,但一直都是知其然不知其所以然。

所以,这次直接把ViewHolder的工作原理,通过简单的demo代码来验证一次,验证后对于图片错乱和闪烁这种问题的成因就很清楚了。

在这里插入图片描述
这幅图就比较清晰的画出了ViewHolder的工作原理。

可以看到,图中左上角item1上面有一条蓝色的线,item7下面也有一条蓝色的线,这两条线就是屏幕的上下边缘,我们在屏幕中能看到的内容就是item1~item7。

当我们控制屏幕向下滚动时,屏幕上的变化是:

  • item1离开了屏幕,紧接着item8进入了屏幕,这是我们看到的。
  • 在item1离开,item8进入的过程中,还有一个我们看不到的过程。当item1离开屏幕时,它会进入Recycler(反复循环器)构件,然后被放到了item8的位置,成为了我们看到的item8。

2. 验证item的变化过程

下面是MainActivity的代码 ,初始化了100条数据

布局内容:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="23dp"android:layout_marginBottom="20dp"android:layout_gravity="center_horizontal"android:textSize="20dp"android:textStyle="bold"android:text="RecyclerView ViewHolder重用机制" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_view"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#ced6e0"/></LinearLayout>

item布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:id="@+id/round_layout"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"><FrameLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/frs_ad_img"android:layout_width="236dp"android:layout_height="420dp"android:alpha="0.5"android:src="@drawable/ic_launcher_background"android:layout_gravity="center_horizontal"android:scaleType="centerCrop" /><!--    广告标记    --><LinearLayoutandroid:id="@+id/fun_ad_lable_pb_layout"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"android:layout_gravity="right|bottom"android:background="#000"android:gravity="center"android:orientation="horizontal"><TextViewandroid:id="@+id/fun_ad_lable_pb"android:layout_width="wrap_content"android:layout_height="wrap_content"android:includeFontPadding="false"android:text="广告"android:textColor="#FFF"android:textSize="10.3dp" /></LinearLayout></FrameLayout></LinearLayout></LinearLayout>

初始化Adapter并设置到RecyclerView

public class MainActivity extends AppCompatActivity {private RecyclerView mRecyclerView;private List<String> mData;private RecyclerAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mRecyclerView = findViewById(R.id.recycler_view);initData();mAdapter = new RecyclerAdapter();mAdapter.setData(mData);LinearLayoutManager layoutManager = new LinearLayoutManager(this);mRecyclerView.setLayoutManager(layoutManager);mRecyclerView.setAdapter(mAdapter);mAdapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {@Overridepublic void onItemClick(int position) {Toast.makeText(MainActivity.this, "Item被点击 " + position, Toast.LENGTH_SHORT).show();}});}private void initData() {mData = new ArrayList<>();for (int i = 1; i <= 100; i++) {mData.add("Item Data 0" + i);}}
}

下面是Adapter部分,为了更方便验证,代码非常简单,ViewHolder里面只有一个TextView「实现了item的点击事件」

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {private List<String> mData = new ArrayList<>();private OnItemClickListener onItemClickListener;public void setOnItemClickListener(OnItemClickListener onItemClickListener) {this.onItemClickListener = onItemClickListener;}@NonNull@Overridepublic RecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, null, false);return new ViewHolder(view);}/*** 填充onCreateViewHolder方法返回的holder中的控件* @param holder* @param position*/@Overridepublic void onBindViewHolder(@NonNull RecyclerAdapter.ViewHolder holder, int position) {Log.d("TAG", "onBindViewHolder: 验证是否重用了");Log.d("TAG", "onBindViewHolder: 重用了" + holder.mTextView.getTag());Log.d("TAG", "onBindViewHolder: 放到了" + mData.get(position));holder.mTextView.setText(mData.get(position));holder.mTextView.setTag(mData.get(position));holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if(onItemClickListener != null) {onItemClickListener.onItemClick(position);}}});}/*** 重写onCreateViewHolder方法,返回一个自定义的ViewHolder** @param holder*/@Overridepublic void onViewRecycled(@NonNull ViewHolder holder) {super.onViewRecycled(holder);Log.d("TAG", "onViewRecycled: " + holder.mTextView.getTag().toString() + ", position: " + holder.getAdapterPosition());}@Overridepublic int getItemCount() {return mData == null ? 0 : mData.size();}public void setData(List<String> itemData) {if (itemData == null || itemData.size() == 0) {return;}mData.addAll(itemData);}class ViewHolder extends RecyclerView.ViewHolder {TextView mTextView;public ViewHolder(View view) {super(view);mTextView = view.findViewById(R.id.item_text);}}public interface OnItemClickListener {void onItemClick(int position);}
}

简单了解上面代码的运行逻辑,并关注onCreateViewHolder()onBindViewHolder()、**onViewRecycled()**三个方法打印的Log日志,下面通过打印的Log分析验证ViewHolder的创建、释放与复用。

当第一次打开应用加载RecyclerView时,可以观察到在屏幕中我们看到的每一个item都经过onCreateViewHolder()创建了一个ViewHolder对象,textView中的tag都为null。下图中红色框框中的Log可以验证。

在这里插入图片描述

这时候我们往下滚动RecyclerView,再看Log。可可以看到,位置0的数据 Item Data 00 和位置2的数据 Item Data 01所在的ViewHolder被释放,位置06和位置07的数据分别被加载,这个时候,由于onBindViewHolder()在为TextView设置数据前先打印了TextView里面的数据,恰恰就是刚才被回收掉的数据,所以可以验证新绑定的两个ViewHolder对象就是刚才被回收掉的两个ViewHolder。
在这里插入图片描述
同理,当我们把屏幕再次往上滚动时,在屏幕下面超出显示范围的item会被回收,并重用到上面的item中。

要注意的是:

每次会额外加载一条数据,如 Item Data 08,虽然屏幕只能显示6条数据,但是会多加载一条。所以只显示到Item Data 07,但是08也加载了,因为Item Data 02向上滑了一半,但是还并未完全消失,08就要显示出来,所以做预加载。


3. 异步网络导致ViewHolder出现图片错乱的原因

通过上面的内容解释,了解了ViewHolder的重用机制,接下来看一段会出现图片错乱的代码示例。

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {private static final String TAG = "MyRecyclerAdapter";private List<String> mData;private Context mContext;private LayoutInflater inflater;public MyRecyclerAdapter(Context context, List<String> data) {this.mContext = context;this.mData = data;inflater = LayoutInflater.from(mContext);}@Overridepublic int getItemCount() {return mData.size();}@Overridepublic void onViewRecycled(MyViewHolder holder) {super.onViewRecycled(holder);Log.d(TAG, "onViewRecycled: "+holder.imageView.getTag().toString()+", position: "+holder.getAdapterPosition());}@Overridepublic void onBindViewHolder(final MyViewHolder holder, final int position) {Log.d(TAG, "onBindViewHolder: 验证是否重用了");Log.d(TAG, "onBindViewHolder: 重用了"+holder.imageView.getTag());Log.d(TAG, "onBindViewHolder: 放到了"+mData.get(position));holder.imageView.setTag(mData.get(position));new AsyncTask<Void, Void, Bitmap>() {@Overrideprotected Bitmap doInBackground(Void... params) {try {URL url = new URL(mData.get(position));Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);holder.imageView.setImageBitmap(bitmap);}}.execute();}@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {Log.d(TAG, "onCreateViewHolder");View view = inflater.inflate(R.layout.item_layout, parent, false);return new MyViewHolder(view);}static class MyViewHolder extends RecyclerView.ViewHolder {ImageView imageView;public MyViewHolder(View view) {super(view);imageView = (ImageView) view.findViewById(R.id.id_img);}}
}

这段代码相对于上一段只是把TextView改成了ImageView,并在onBindViewHolder()时异步加载一张网络图片,当加载完毕把图片放置到ImageView中显示。

在不了解ViewHolder重用机制之前,这段代码看似没有什么问题,但事实上这段代码由于ViewHolder重用机制的存在,并不能如期运行。

下面使用这段代码来分析一下场景。

3.1 场景分析

【场景A】

  1. 第一次运行,RecyclerView载入,不做任何触摸操作
  2. Adapter经过onCreateViewHolder()创建了上面我们能看到的8个ViewHolder对象,并且在onBind时启动了8条线程加载图片
  3. 8张图片全部加载完毕,并且显示到对应的ImageView上
  4. 控制屏幕向下滚动,第1、第2个item离开屏幕可视区域,第9、第10个item进入屏幕可视区域
  5. 第1、第2个item被回收,重用到第9、第10个item。第9、第10个item显示的图片是第1和第2个item的图片!!!
  6. 开启了两条线程,加载第9、第10张图片。等待几秒,第9、第10个item显示的图片突然变成了正确的图片!

以上过程是场景A,经过拆分细化,非常容易看出问题所在。如果当前网络速度很快,第6个步骤的加载速度在1秒甚至0.5秒内,就会造成人眼看到的图片闪烁问题出现,第9、第10个item的图片闪了一下变成了正确的图片。


【场景B】

  1. 第一次运行,RecyclerView载入
  2. Adapter经过onCreateViewHolder()创建了上面我们能看到的8个ViewHolder对象,并且在onBind时启动了8条线程加载图片
  3. 7张图片加载完毕,还有1张未加载完(已知图片一加载速度异常慢)
  4. 控制屏幕向下滚动,第1、第2个item离开屏幕可视区域,第9、第10个item进入屏幕可视区域
  5. 第1、第2个item被回收,重用到第9、第10个item。闪烁问题不再重复说,第9、第10张图片加载完毕(看上去一切正常)
  6. 等待几秒,第一张图片终于加载完成,第9个item突然从正确的图片九变成不正确的图片一 !!!

以上过程是场景B,问题出现在加载第一张图片的线程T,持有了item1的ImageView对象引用,而这张图片加载速度非常慢,直到item1已经被重用到item9后,过了一段时间,线程T才把图片一加载出来,并设置到item1的ImageView上,然而线程T并不知道item1已经不存在且变成了item9,于是,图片发生错乱了。


【场景C】

  1. 第一次运行,RecyclerView载入
  2. Adapter经过onCreateViewHolder()创建了上面我们能看到的8个ViewHolder对象,并且在onBind时启动了8条线程加载图片
  3. 忽略图片加载情况,直接向下滚动,再向上滚动,再向下滚动,来回操作
  4. 由于离开了屏幕的item是随机被回收并重用的,所以向下滚动时我们假设item1、item3被回收重用到item9、item10,item2、item4被回收重用到item11、item12
  5. 向上滚动时,item9、item12被回收重用到item1、item2,item10、item11被回收重用到item3、item4
  6. 多次上下滚动后,停下,最后发现某一个item的图片在不停变化,最后还不一定是正确的图片

以上过程是场景C,问题出现在ViewHolder的回收重用顺序是随机的,回收时会从离开屏幕范围的item中随机回收,并分配给新的item,来回操作数次,就会造成有多条加载不同图片的线程,持有同一个item的ImageView对象,造成最后在同一个item上图片变来变去,错乱更加严重。


3.2 解决方法

解决方法其实有很多种,这里列出两种情况:

  1. 当item还在加载图片的过程中,被移出屏幕可视范围,不需要继续加载这张图片了,可以在onRecycled中取消图片的加载。这样就不会造成图片加载完成设置到其他item的ImageView中了。
  2. 每一个经过屏幕可视区域的item,加载的图片都要放进缓存中,即使item离开了可视区域,也要加载完毕并放入缓存中,方便下次浏览时能快速加载。每次onBind时对ImageView设置Tag标记,如果Tag标记已经被更改,旧线程加载好的图片不再设置到ImageView中。

当然以上两种情况都别忘了先设置图片占位符(设置默认视图),防止回收item的图片直接显示到新item中。

3.3 解决方式一

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {private static final String TAG = "MyRecyclerAdapter";private List<String> mData;private Context mContext;private LayoutInflater inflater;public MyRecyclerAdapter(Context context, List<String> data) {this.mContext = context;this.mData = data;inflater = LayoutInflater.from(mContext);}@Overridepublic int getItemCount() {return mData.size();}@Overridepublic void onViewRecycled(MyViewHolder holder) {super.onViewRecycled(holder);AsyncTask asyncTask = (AsyncTask) holder.imageView.getTag(1);// 取消加载asyncTask.cancel(true);}@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = inflater.inflate(R.layout.item_layout, parent, false);return new MyViewHolder(view);}@Overridepublic void onBindViewHolder(final MyViewHolder holder, final int position) {//先设置图片占位符holder.imageView.setImageDrawable(mContext.getDrawable(R.mipmap.ic_launcher));AsyncTask asyncTask = new AsyncTask<Void, Void, Bitmap>() {@Overrideprotected Bitmap doInBackground(Void... params) {try {URL url = new URL(mData.get(position));Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);holder.imageView.setImageBitmap(bitmap);}};holder.imageView.setTag(1,asyncTask);asyncTask.execute();}static class MyViewHolder extends RecyclerView.ViewHolder {ImageView imageView;public MyViewHolder(View view) {super(view);imageView = (ImageView) view.findViewById(R.id.id_img);}}
}

3.4 解决方式二

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {private static final String TAG = "MyRecyclerAdapter";private List<String> mData;private Context mContext;private LayoutInflater inflater;public MyRecyclerAdapter(Context context, List<String> data) {this.mContext = context;this.mData = data;inflater = LayoutInflater.from(mContext);}@Overridepublic int getItemCount() {return mData.size();}@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = inflater.inflate(R.layout.item_layout, parent, false);return new MyViewHolder(view);}@Overridepublic void onBindViewHolder(final MyViewHolder holder, final int position) {// 先设置图片占位符holder.imageView.setImageDrawable(mContext.getDrawable(R.mipmap.ic_launcher));final String url = mData.get(position);// 为imageView设置Tag,内容是该imageView等待加载的图片urlholder.imageView.setTag(url);AsyncTask asyncTask = new AsyncTask<Void, Void, Bitmap>() {@Overrideprotected Bitmap doInBackground(Void... params) {try {URL url = new URL(mData.get(position));Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());return bitmap;} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);// 加载完毕后判断该imageView等待的图片url是不是加载完毕的这张// 如果是则为imageView设置图片if(url.equals(holder.imageView.getTag())) {holder.imageView.setImageBitmap(bitmap);} // 否则说明imageView已经被重用到其他item}}.execute();}static class MyViewHolder extends RecyclerView.ViewHolder {ImageView imageView;public MyViewHolder(View view) {super(view);imageView = (ImageView) view.findViewById(R.id.id_img);}}
}

上面的解决方式,是最简单的使用异步线程加载图片,对于加载图片有很多第三方库可以使用,如Picasso、Fresco、Glide等,我们也可以使用这些第三方库来加载图片,但使用第三方库加载的本质还是异步加载,所以如果处理不当也会出现图片闪烁等问题,大家可以使用上面的场景ABC等细化分解的步骤来分析错误,相信很容易就能找到问题。

3.5 注意内存泄漏的风险

对于上面的Demo代码,其实是存在内存泄漏风险的,如果需要使用建议把AsyncTask写成静态内部类,以及Adapter初始化时使用ApplicationContext作为参数传入,不要使用Activity作为Context参数。




4. RecyclerView网络图像刷新会闪烁

4.1 全局刷新

当你使用.notifyDataSetChanged()刷新列表时,会更新所有的数据。耗资源不说,关键是整个界面图像重新加载,给用户莫名其妙的感觉,影响体验和心情。

都知道RecyclerView的Item是可以复用,但是如何复用,可能就不大清楚了。每个Item被复用是随机的,它移出屏幕外被回收后,等待再次使用。全局刷新,会让回收的Item加载内容,没回收的可能内容被别的Item先加载了,只能回收后加载。一瞬间Item的位置全乱了,都得重新加载内容,尤其图像加载较慢,你就感觉整个世界胡乱地闪了一下。

知道原因,想解决就不难了。给每个Item一个固定的ID,刷新时与位置对应,别瞎跑就可以了。重写Adapter的getItemId(int position)方法,返回position作为ID,然后再设置Adapter的setHasStableIds(true)即可。


4.2 局部刷新

RecyclerView的Adapter新增了不少局部刷新的方法,具体如下:

  • notifyItemChanged(int position),更新列表position位置上的数据时调用,伴有渐变动画效果
  • notifyItemInserted(int position),列表position位置添加一条数据时调用,伴有动画效果
  • notifyItemRemoved(int position),列表position位置移除一条数据时调用,伴有动画效果
  • notifyItemMoved(int fromPosition, int toPosition),列表fromPosition位置的数据移到toPosition位置时调用,伴有动画效果
  • notifyItemRangeChanged(int positionStart, int itemCount),列表从positionStart位置到itemCount数量的列表项进行数据刷新,伴有渐变动画效果
  • notifyItemRangeInserted(int positionStart, int itemCount),列表从positionStart位置到itemCount数量的列表项批量添加数据时调用,伴有动画效果
  • notifyItemRangeRemoved(int positionStart, int itemCount),列表从positionStart位置到itemCount数量的列表项批量删除数据时调用,伴有动画效果

使用局部刷新解决了Item错乱的问题,并且减少了额外的资源消耗,可是闪烁仍然存在。只不过导致的原因变了,是由于渐变动画效果增加了展示图像的时间。所以解决方法也很简单,就是取消或屏蔽过渡动画。提供两种常用的方法供大家参考:

// 第一种,直接取消动画
ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}// 老版本的支持库
recyclerView.getItemAnimator().setSupportsChangeAnimations(false);// 第二种,设置动画时间为0
recyclerView.getItemAnimator().setSupportsChangeAnimations(false);

【相关问题文章参考】

  • Android ListView异步加载图片乱序问题,原因分析及解决方案
  • RecyclerView局部刷新的坑-https://juejin.cn/post/6844903506781061128

http://chatgpt.dhexx.cn/article/BvgawsZy.shtml

相关文章

CCF CSP认证成绩代替考研复试机试!

最近整理考研资料&#xff0c;慢慢了解到CSP在考研方面还是挺有用的&#xff0c;算是给自己多了些机会。 已参加认证的高校对CSP认证成绩给予高度认可&#xff0c;部分知名院校通过不同方式对CSP认证成绩给予认可&#xff0c;其中包括&#xff1a; &#xff08;1&#xff09;…

CCF-CSP认证 202303 500分题解

202303-1 田地丈量&#xff08;矩形面积交&#xff09; 矩形面积交x轴线段交长度*y轴线段交长度 线段交长度&#xff0c;相交的时候是min右端点-max左端点&#xff0c;不相交的时候是0 #include<bits/stdc.h> using namespace std; int n,a,b,ans,x,y,x2,y2; int f(in…

谈一下两次CSP认证从180分到380分的感想

最近联系我的小可爱们比较多&#xff0c;我用qq建了一个ccf csp考试交流群&#xff0c;群号673612216&#xff0c;如果感觉有用可以加一下哦~~ 欢迎访问我的CCF认证考试题解目录哦 https://blog.csdn.net/richenyunqi/article/details/83385502&#xff0c;目前正在准备考研&am…

第22次 CCF CSP认证一二题题解及感悟

第22次 CCF CSP认证一二题题解及感悟 第一题灰度直方图题目重述题目分析代码及注释&#xff08;C&#xff09; 第二题邻域均值题目重述题目分析代码及注释&#xff08;C&#xff09;感悟 第一题灰度直方图 题目重述 一幅长宽分别为 n 个像素和 m 个像素的灰度图像可以表示为一…

CCF CSP认证2022年12月题解 现值计算、训练计划、JPEG 解码

题目 http://118.190.20.162/home.page T1 现值计算 思路 根据题意第 k k k年的 x x x元的当前价值为 x ( 1 i ) − k x\times (1i)^{-k} x(1i)−k计算各个价值&#xff0c;最后求和。 代码 int main() {int n; double i;scanf("%d %lf", &n, &i);i…

第29次CCFCSP认证经验总结

鄙人有幸参加了由中国计算机学会举办的第29次计算机软件能力认证考试&#xff0c;在此进行一些考试细节和经验的总结。 如果没有仔细了解过的小白去网上搜索CCFCSP&#xff0c;可能出现的是CSP-J/S&#xff0c;但是详细了解会发现&#xff0c;首先CSP-J/S分初试和复试&#xff…

第28次csp认证T3 JPEG 解码解析

第28次csp认证T3 JPEG 解码解析 题目说明 问题比较长&#xff0c;就只放个链接吧&#xff1a;http://118.190.20.162/view.page?gpidT158 经验分享 做这种大模拟的题目&#xff0c;对于经验不是很丰富的新手来说&#xff0c;更应该着眼于得分点&#xff0c;先做那些问题简单、…

csp认证考试准备Day-3

昨天复习了一点点&#xff0c;今天浅浅做几个第一道的真题吧 &#xff08;1&#xff09;202212-1 #include<bits/stdc.h> using namespace std; int main() {double n,i,b;scanf("%lf", &n);scanf("%lf", &i);double a[60];for(int j0;j<…

csp认证考试准备Day-1

今天&#xff0c;开启了我的第一个专栏&#xff0c;用来记录我的2023年3月的csp认证考试。 语言&#xff1a;c 本人状况&#xff1a;半学期几乎没敲过代码&#xff0c;学过c和数据结构&#xff0c;csp第一题应该能做出来。 目标&#xff1a;保证在csp考试时做出一二题&#…

第23次CSP认证题解

这是我第一次参加CSP&#xff0c;一共得了260分&#xff0c;100,70,70,20,0。这两天试着写一下题解&#xff0c;大家哪里看不懂直接留言问我就好。 目录 第一题&#xff1a;数组推导&#xff08;100分&#xff09;第二题&#xff1a;非零段划分&#xff08;100分&#xff09;第…

csp认证真题

出行计划 要在t时刻进入场所&#xff0c;获得核酸检测结果的时间点&#xff08;tk应该在[t-c1,t]内&#xff08;上段文字中c值为24&#xff09;&#xff0c;核酸检测结果才能生效。由于获得核酸检测结果的时刻>1&#xff08;因为等待核酸检测结果的时间k>0&#xff0c;所…

【经验】CCF CSP认证问题

202109-4收集卡牌 状压dp&#xff0c;注意保留10位小数&#xff0c;是样例里给出的最长的&#xff0c;五位也不行&#xff0c;double保留到小数点后15位以后就不准了 202104-2 邻域均值 要利用前缀和&#xff0c;不然会超时 202012-2 期末预测之最佳阈值 也是要利用前缀和&…

有关CCF的CSP认证

有关CCF的CSP认证 一、CSP认证考点的知识要求 在数据结构中&#xff0c;线性表是基础&#xff0c;树是常考点&#xff0c;集合和映射要夕纪学。 背包问题&#xff08;动态规划&#xff09; 考试要求 二、考试题型 第一题&#xff1a;一般为水题&#xff0c;把C学扎实便可…

记 CSP 认证

欢迎访问我的CCF认证解题目录 现在越来越忙了&#xff0c;估计后面也不参加了&#xff0c;纯粹是记录贴。 先晒一下成绩吧&#xff0c;至于为什么参加这么多次&#xff0c;主要是学校可以报销&#xff0c;干就完了&#xff0c;哈哈。 分别是 17、18、20、21 第一次参加 c…

CSP认证

【CSP】试题编号 202212-2-训练计划 题目&#xff1a;训练计划计算最早/最晚开始时间最早开始时间发散最晚开始时间 代码与上机代码上机结果 题目&#xff1a;训练计划 此题目样例有坑&#xff1a;样例中没有正确输出过一个最晚开始时间 所以在最开始处理问题的时候&#xff0…

CCF CSP认证

文章目录 :heart:[CCF CSP认证 (cspro.org)](https://www.cspro.org/):heart:1.主办单位2.认证目的3.认证内容4.认证方式5.准备认证上机环境6. 选择考试语言7. 选择编译环境8. 选择IDE9.认证前模拟练习10.成绩效力&#xff1a; ❤️CCF CSP认证 (cspro.org)❤️ 1.主办单位 中…

四大含金量高的算法证书考试

证书考试推荐 一、PAT 计算机程序设计能力测试二、CCF CSP认证三、团体程序设计天梯赛四、蓝桥杯大赛 一、PAT 计算机程序设计能力测试 官网&#xff1a;PAT 计算机程序设计能力测试 PAT为浙江大学出的一款程序设计的测试网站&#xff0c;分为乙级、甲级、顶级三种&#xff0…

2阶实对称矩阵特征值和特征向量的简单求解方法

2阶实对称矩阵特性 定理&#xff1a;2阶实对称矩阵H的特征值是实数 H[a,b;b,c] a,b,c是实数&#xff0c;λ 是特征值 A[a-λ,b;b,c-λ] 特征值求解方法为&#xff1a;(a- λ )(c- λ) - b2 0 求解方程得到两个根为&#xff1a;λ&#xff08;ac&#xff09;&…

求解矩阵特征值的QR算法

1. 算法原理介绍&#xff1a; 1. Householder变换&#xff1a; 2. Givens变换&#xff1a; 3. 矩阵的QR分解 4. 计算特征值的QR方法 5. 上Hessenberg矩阵方法&#xff1a; 2. 实施过程&#xff1a; 1. 约化过程&#xff1a; 1. Householder变换&#xff1a; 2. Givens变换&a…

【OpenCV4】计算对称矩阵特征值和特征向量 cv::eigen() 用法详解和代码示例(c++)

函数原型&#xff1a; bool cv::eigen ( InputArray src,OutputArray eigenvalues,OutputArray eigenvectors noArray() ) 解析&#xff1a; src&#xff1a;输入矩阵&#xff0c;只能是 CV_32FC1 或 CV_64FC1 类型的方阵&#xff08;即矩阵转置后还是自己&#xff09;eig…